TCP的特点是面向连接、字节流、一对一和可靠传输。
当发送端应用程序连续执行多次写操作时,TCP模块先将这些数据放入TCP发送缓冲区中,当TCP模块真正开始发送数据时,发送缓冲区中这些等待发送的数据可能被封装成一个或多个TCP报文段发出。因此,TCP模块发送出的TCP报文段的个数和应用程序执行的写操作次数之间没有固定的数量关系。
当接收端收到一个或多个TCP报文段后,TCP模块将它们携带的应用程序数据按照TCP报文段的序号依次放入TCP接收缓冲区中,并通知应用程序读取数据,接收端应用程序可以一次性将TCP接收缓存中的数据全部读出,也可以分多次读取,这取决于用户指定的应用程序读缓冲区的大小,因此,应用程序执行的读操作次数和TCP模块接收到TCP报文段个数之间也没有固定的数量关系。
字节流的概念:发送端执行的写操作次数和接收端执行的读操作次数之间没有任何数量关系。应用程序对数据的发送和接收是没有边界限制的。
(UDP则是,发送端应用程序每执行一次写操作,UDP模块就将其封装成一个UDP数据报并发送之,接收端必须及时针对每一个UDP数据报执行读操作(通过recvfrom系统调用),否则就会发生丢包。)并且,如果用户没有指定足够的应用程序缓冲区来读取UDP数据,则 UDP数据报将被截断。
TCP传输是可靠的:TCP协议采用发送应答机制,即发送端发送的每个TCP报文段都必须得到接收方的应答,才认为这个TCP报文段传输成功。其次,TCP协议采用超时重传机制,发送端在发送出一个TCP报文段之后启动定时器,如果在定时时间内未收到应答,它将重发该报文段,因为TCP报文段最终是以IP数据报发送的,而IP数据报到达接收端可能乱序、重复,所以TCP协议还会对接收到的TCP报文段重排、整理,再交付给应用层。
TCP固定头部结构:
16位端口号:告知主机该报文段是来自哪里(源端口)以及传给哪个上层协议或应用程序(目的端口)的。进行TCP通信时,客户端通常使用系统自带选择的临时端口号,而服务器则使用知名服务端口号。
32位序号:一次TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号。
32位确认号:用作对另一方发送来的TCP报文段的响应。其值是收到的TCP报文段的序号值加1。
4位头部长度:标识该TCP头部有多少个32Bit字(4字节)。
6位标志位包含如下几项:
URG标志,表示紧急指针是否有效。
ACK标志,表示确认号是否生效。
PSH标志,提示接收端应用程序应该立即从TCP接收缓冲区中读走数据,为接手后续数据腾出空间。
RST标志,表示要求对方重新建立连接,我们称携带RST标志的TCP报文段为复位报文段。
SYN标志,表示请求建立一个连接,我们称携带SYN标志的TCP报文段为同步报文段
FIN标志,表示通知对方本端要关闭连接了,我们称携带SYN标志的TCP报文段为同步报文段。
16位窗口大小:是TCP流量控制的一个手段。这里说的窗口,指的是接收通告窗口。他告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
16位校验和:由发送端填充,接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程中是否损坏(包括头部和数据部分)。
16位紧急指针:是一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。这个字段是紧急指针相对当前序号的偏移,可以称之为紧急偏移,TCP的紧急指针是发送端向接收端发送紧急数据的方法。
TCP头部的最后一个选项字段是可变长的可选信息,这部分最多包含40字节,因为TCP头部最长是60字节(其中还包含前面讨论的20字节的固定部分)。
典型的TCP头部选项结构:
选项的第一个字段kind说明选项的类型。有的TCP选项没有后面两个字段。仅包含1字节的kind字段,第二个字段length(如果有的话)指定该选项的总长度,该长度包括kind字段和length字段占据的2字节,第三个字段info(如果有的话)是选项的具体信息,常见的TCP选项有7种。
kind=0是选项表结束选项
kind=1是空操作选项,没有特殊含义,一般用于将TCP选项的总长度填充为4字节的整数倍。
kind=2是最大报文段长度选项,TCP连接初始化时,通信双方使用该选项来协商最大报文段长度(MMS)。
kind=3是窗口扩大因子选项。可以通过修改/proc/sys/net/ipv4/tcp_window_scaling 内核变量来启动或关闭窗口扩大因子选项。
kind=4是选择性确认(SACK)选项。可以通过修改/proc/sys/net/ipv4/tcp_sack内核变量来启动或关闭选择性确认选项。
kind=5是 SACK实际工作的选项。
kind=8是时间戳选项。可以通过修改/proc/sys/net/ipv4/tcp_timestamps内核变量来启动或关闭时间戳选项。
TCP连接是全双工的,所以它允许两个方向的数据传输被独立关闭。通信的一端可以发送结束报文段给对方,告诉他本端已经完成了数据的发送,但允许继续接收来自对方的数据,直到对方也发送结束报文段以关闭连接。TCP连接的这种状态称为半关闭状态。
命令netstat
查看TCP连接的状态。
粗虚线表示典型的服务器端连接的状态转移;粗实线表示典型的客户端连接的状态转移。CLOSED是一个假想的起始点,并不是一个实际的状态。
连接停留在FIN_WAIT_2
状态的情况可能发生在:客户端执行半关闭后,未等服务器关闭连接就强行退出了。此时客户端连接由内核来接管,可称为孤儿连接(和孤儿进程类似)。Linux为了防止孤儿连接长时间存留在内核中,定义了两个内核变量:/proc/sys/net/ipv4/tcp_max_orphans
和/proc/sys/net/ipv4/tcp_fin_timeout
。前者指定内核能接管的孤儿连接数目,后者指定孤儿连接在内核中生存的时间。