APUE学习--网络编程(3)
本篇文章介绍TCP通信。
上文提到传输层的两个协议TCP和UDP,UDP是无连接的已经介绍过,TCP是面向连接的,阐述建立连接和断开连接前先来看下TCP报文头的结构。
报文头在linux的定义在/usr/include/netinet/tcp.h中:
struct tcphdr
{
u_int16_t source; //源端口号
u_int16_t dest; //目的端口号
u_int32_t seq; //32位的TCP报文序列号
u_int32_t ack_seq; //32位的TCP报文确认序列号
u_int16_t res1:4; //保留位
u_int16_t doff:4; //首部长度
u_int16_t fin:1; //fin置1表示该报文用于申请断开TCP连接
u_int16_t syn:1; //syn置1表示该报文用于申请建立TCP连接
u_int16_t rst:1; //rst置1表示该报文用于申请重建TCP连接
u_int16_t psh:1; //psh置1表示该报文的优先级较高(用于发送紧急报文)
u_int16_t ack:1; //ack置1表示该报文具有确认的功能,此时确认序列号有效
u_int16_t urg:1; //urg置1使紧急指针有效
u_int16_t res2:2; //保留位(加上res1共6位)
u_int16_t window; //窗口大小,用于流量控制
u_int16_t check; //tcp报文的校验和
u_int16_t urg_ptr; //紧急指针(是一个偏移量),序列号到紧急指针之间的数据为紧急数据,紧急指针后的数据才是正常数据
};
可以看出来,TCP报文首部设计的功能要比UDP报文首部复杂的多(可靠自然带来大量的额外开销),在建立连接中我们比较关心的是32位的序列号、32位的确认序列号、SYN位、ACK位,通过下面的图形我们来看下TCP建立连接的三次握手过程(三次握手指的是三次报文的传输),其中发起连接的一端我们称为主动端,等待连接的一端我们称为被动端。
第一次握手:主动端向被动端发送一个syn置1,序列号为x的一个tcp报文
第二次握手:被动端向主动端回溯一个ack置1,确认序列号为x+1的tcp报文同时将该报文的syn置1并生成一个序列号y,以此使该报文又具有了发起连接的功能
第三次握手:主动端再向被动端回溯一个ack置1,确认序列号为y+1的的确认报文,到此完成了tcp连接的建立。
简单总结下:完成tcp建立连接需要两端都进行以此连接申请和申请的确认,但总会有一个主动端先申请建立连接。下面我们来看下如何通过函数调用来完成整个建立的阶段。
先来看下被动端,有一个阶段是等待连接请求的阶段,通过listen()函数开启连接的监听等待:
int listen(int sockfd, int backlog);
参数sockfd是在哪个套接字上实现监听,backlog是最多能够建立几个连接(已经完成三次握手的)。listen()函数并不会阻塞等待,而是开启监听等待,开启后的等待过程是由内核的协议栈完成的,此时sockfd套接字已成为监听描述符,该描述符的可读条件变成有连接完成,当有连接完成时通过accept()函数来读出完成的连接并返回该TCP套接字描述符:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数sockfd是监听描述符,addr是主动端的网络地址,addrlen是地址长度。该函数被调用后会阻塞至sockfd可读(即有连接完成),返回tcp套接字描述符。注意在TCP的被动端至少有两个套接字描述符,一个是监听描述符(用socket()创建并用listen开启监听状态),一个是tcp通信的描述符(由内核创建并由accept返回)。
连接建立之后,套接字的通信双方已经确定,在通信时就不必对方的网络地址,直接使用read()/write()传输数据即可。
TCP连接的被动端的函数调用过程:socket()创建监听描述符-->bind()绑定本地网络地址-->listen()开启监听状态-->accept()获得建立的tcp连接的套接字描述符-->read()/write()进行数据通信-->close()关闭套接字(监听描述符结束监听状态,tcp连接描述符断开TCP连接)。
在来说下TCP的主动端,主动端使用connect()函数发起tcp连接的建立:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数sockfd是套接字描述符(由socket()创建),addr是被动端的网络地址,addrlen是addr的长度。该函数将阻塞至内核完成三次握手,主动端只有一个描述符(无监听描述符)。
TCP连接的主动端的函数调用过程:socket()创建套接字描述符-->connect()发起连接请求-->read()/write()进行数据通信-->close()关闭套接字(断开TCP连接)。
下面我们看下断开连接的四次握手,从之前提到的函数调用过程中可以发现,无论是主动端还是被动段都可以发起断开连接,下面以主动端发起断开连接请求为例,被动端先发起是一样的。
第一次握手:主动端向被动发送fin置1,序列号为x的断开连接报文
第二次握手:被动端向主动端回溯一个ack置1,确认序列号为x+1的确认报文,主动端接到后为半关闭状态
第三次握手:被动端过一端时间后再向主动端发送fin置1,序列号为y的断开连接报文
第四次握手:主动端向被动端回溯一个ack置1,确认序列号为y+1的确认报文,此时连接为全关闭状态
整个过程是在调用close()之后由内核完成,但close()并不阻塞。这样在编程时可能会出现的这种情况,被动端在绑定网络地址时出现地址已经被占用,过一段时间后才能绑定成功,原因就是上次的四次握手还未完成。解决办法一个使用setsockopt()函数设置地址可被重复绑定,具体操作如下:
int on = 1;
setsockopt(sockfd, SOL_SOCK, SO_REUSEADDR, &on, sizeof(on));
该操作应该在socket()和bind()之间调用,其中SOL_SOCK表示是套接字的通用选项,SO_REUSEADDR表示的是地址重用选项,on=1表示开启。
APUE学习--网络编程(3)的更多相关文章
- Java学习——网络编程
Java学习——网络编程 摘要:本文主要介绍了什么是网络编程,以及如何使用Java语言进行网络编程. 部分内容来自以下博客: https://www.cnblogs.com/renyuan/p/269 ...
- Python学习---网络编程 1217【all】
OSI七层模型: 物理层, 数据链路层, 网络层,传输层,会话层,表达层,应用层 应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等 传输层:TCP,UDP 网络层:I ...
- 初识Socket通信:基于TCP和UDP协议学习网络编程
学习笔记: 1.基于TCP协议的Socket网络编程: (1)Socket类构造方法:在客户端和服务器端建立连接 Socket s = new Socket(hostName,port);以主机名和端 ...
- java学习——网络编程UDP
UDP 将数据及源和目的封装成数据包中,不需要建立连接 每个数据报的大小限制在64k内 因无连接,是不可靠协议 不需要建立连接,速度快 TCP 建立连接,形成传输数据的通道 在连接中进行大数据量传输 ...
- Java 网络编程学习总结
新手一枚,Java学习中,把自己学习网络编程的知识总结一下,梳理下知识,方便日后查阅,高手莫进. 本文的主要内容: [1] 网络编程认识 [2] TCP/IP编程 ...
- python网络编程学习《一》
最近,刚实习完,很喜欢实验楼,但是自己的方向仍然不能确定,自己觉得可选择的空间很大,尽管已经是大四的人了,想到别人都在忙着买职业装,买高跟鞋面试,学习化妆什么的,看看自己,反而开始慢慢关注运动,食疗以 ...
- Java学习之网络编程实例
转自:http://www.cnblogs.com/springcsc/archive/2009/12/03/1616413.html 多谢分享 网络编程 网络编程对于很多的初学者来说,都是很向往的一 ...
- Python学习笔记(二)网络编程的简单示例
Python中的网络编程比C语言中要简洁很多,毕竟封装了大量的细节. 所以这里不再介绍网络编程的基本知识.而且我认为,从Python学习网络编程不是一个明智的选择. 简单的TCP连接 服务器代码如 ...
- 五分钟学Java:如何学习Java面试必考的网络编程
原创声明 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 本文思维导图 简介 Java作为一门后端语言,对于网络编程的支持是必不可少的,但是,作为一个经常CRUD的Java工程师,很多时候都不 ...
随机推荐
- innerXml,outerXml,innerText的不同
原文:innerXml,outerXml,innerText的不同 昨天看到咱们园子里有一个仁兄写的关于xml的有关操作,在读的过程中,由于是初学者有不明白的地方就查资料,发现自己多innerXml, ...
- CDialogBar(对话条)和CReBar(伸缩条)的编程
对话条是工具栏和无模式对话框相结合的产物,对话条和对话框类似,包含有标准的Windows控件,并且可以通过创建对话框模板来表示对话条. 伸缩条功能很强大,我们可以在伸缩条中直接增加CToolBar,C ...
- Mono for Android 初学遇到的问题
1.搭建开发环境: 在win7系统中,VS2012 可以用 C# 开发Android 应用程序,mono for andriod 破解成功. 在win server 2008 系统中 破解不成功,具体 ...
- 不同数据库oracle mysql SQL Server DB2 infomix sybase分页查询语句
在不同数据库中的使用的分页查询语句: 当前页:currentpage 页大小:pagesize 1. Oracle数据库 select * from (select A.*,rownum rn fro ...
- POJ1273_Drainage Ditches(网络流)
Drainage Ditches Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 54887 Accepted: 2091 ...
- Ajax - 异步调用后台程序 -JSON
在ASP.NET使用ajax时基本上每个操作都要新建一个.ashx处理程序,页面很多,每个页面的操作也很多,这样的话项目就会产生新建很多很多的.ashx页面,能不能把方法写在后台中,然后Jquery直 ...
- 深度RAMOS,把操作系统全部安装在内存上
你看下深度RAMOS就知道了 RAMOS+音速启动+绿色软件+云端 很爽 http://www.shenduwin7.com/jiaocheng/52.html
- 嵌入式linux多进程编程
嵌入式linux多进程编程 在主程序显示文本菜单.提供例如以下服务.要求每一个服务都通过生成子进程来提供. 服务包含:日历信息显示,日期信息显示,推断闰年服务,文件复制功能,数字排序功能.退出功能. ...
- Common Lisp学习笔记(0):从SLIME开始 | 优哉·幽斋
Common Lisp学习笔记(0):从SLIME开始 | 优哉·幽斋 Common Lisp学习笔记(0):从SLIME开始
- hibernate级联保存问题,出错not-null property references a null or transient value
Servlet.service() for servlet default threw exception org.hibernate.PropertyValueException: not-null ...