TCP协议
TCP服务
TCP服务特点
-
面向连接(减小内核资源的反复分配开销)(确认通信实体的存在)
使用TCP协议通信的双方必须先建立连接,然后才能开始数据的读写。双方都必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输。TCP连接是全双工的,即双方的数据读写可以通过一个连接进行。完成数据交换之后,通信双方都必须断开连接以释放系统资源。TCP协议的这种连接是一对一的,所以基于广播和多播(目标是多个主机地址)的应用程序不能使用TCP服务。而无连接协议UDP则非常适合于广播和多播。
-
字节流(对应用数据没有长度限制)
当发送端应用程序连续执行多次写操作时, TCP模块先将这些数据放人TCP发送缓冲区中。当TCP模块真正开始发送数据时,发送缓冲区中这些等待发送的数据可能被封装成一个或多个TCP报文段发出。因此,TCP模块发送出的TCP报文段的个数和应用程序执行的写操作次,数之间没有固定的数量关系。而对于UDP,每次应用数据写入缓存区,就会被发送,因此应用数据长度不应大于UDP。
-
可靠传输
-
超时重传机制
发送端在发送出一个TCP报文段之后启动定时器,如果在定时时间内未收到应答,它将重发该报文段。
-
序号机制(序号、确认号)
最后,因为TCP报文段最终是以IP数据报发送的,而IP数据报到达接收端可能乱序、重复,所以TCP协议还会对接收到的TCP报文段重排、整理,再交付给应用层。即发送端发送每个TCP报文段都必须得到接收方的应答,才认为这个TCP报文段传输成功。
-
数据检验
CRC校验全部数据。
-
流量控制(作用于接收者)
滑动窗口协议既保证了分组无差错、有序接收,也实现了流量控制(防止接收者收不过来)。
-
拥塞控制(作用于网络)
避免过量发送导致网络负载过大(防止网络带宽发不过来)。
-
TCP头部信息
TCP头部信息出现在每个TCP报文段中,用于指定通信的源端端口号、目的端端口号,管理TCP连接,控制两个方向的数据流。

-
16位端口号(标识传递的应用程序)
-
32位序号(SQN)
假设主机A和主机B进行TCP通信,A发送给B的第一个TCP报文段中,序号值被系统初始化为某个随机值ISN (Initial SequenceNumber,初始序号值)。那么在该传输方向上(从A到B),后续的TCP报文段中序号值将被系统设置成ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移。
-
32位确认号(ACK)
32位确认号(acknowledgement number):用作对另一方发送来的TCP报文段的响应。其值是收到的TCP报文段的序号值加1,假设主机A和主机B进行TCP通信,那么A发送出的TCP报文段不仅携带自己的序号,而且包含对B发送来的TCP报文段的确认号。反之,B发送出的TCP报文段也同时携带自己的序号和对A发送来的报文段的确认号。
-
标志位
-
URG标志(紧急指针)
表示紧急指针(urgent pointer)是否有效。
-
ACK标志
表示确认号是否有效。我们称携带ACK标志的TCP报文段为确认报文段。
-
PSH标志
提示接收端应用程序应该立即从TCP接收缓冲区中读走数据,为接收后续数据腾出空间(如果应用程序不将接收到的数据读走,它们就会一直停留在TCP接收缓冲区中)。
-
RST标志
表示要求对方重新建立连接。我们称携带RST标志的TCP报文段为复位报文段。
-
SYN标志(同步报文)
表示请求建立一个连接。我们称携带SYN标志的TCP报文段为同步报文段。
-
FIN标志(结束报文)
表示通知对方本端要关闭连接了。我们称携带FIN标志的TCP报文段为结束报文段
-
-
16位窗口大小
窗口大小(window size):是TCP流量控制的一个手段。这里说的窗口,指的是接收通告窗口(Receiver Window, RWND)。它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
-
16位校验和(CRC)
-
16位紧急指针(带外数据)
16位紧急指针(urgent pointer):是一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。因此,确切地说,这个字段是紧急指针相对当前序号的偏移,不妨称之为紧急偏移。TCP的紧急指针是发送端向接收端发送紧急数据的方法。我们将在后面讨论TCP紧急数据。
头部选项不在这里展开讨论。
TCP连接建立和关闭
TCP和IP连接建立和关闭
一般而言,TCP连接是由客户端发起,并通过三次握手建立(特殊情况是所谓同时打开(1)的。TCP连接的关闭过程相对复杂一些。可能是客户端执行主动关闭,比如前面的例子;也可能是服务器执行主动关闭,比如服务器程序被中断而强制关闭连接:还可能是同时关闭(和同时打开一样,非常少见)。

总结:
实际上是要保证两端的收发没有问题,应该是要两发两收,然而中间一次收发合并,因此是三次握手。而关闭连接的话,在其中设置了一个半关闭状态,所以没有进行合并。
半关闭状态
TCP连接是全双工的,所以它允许两个方向的数据传输被独立关闭。换言之,通信的一端可以发送结束报文段给对方,告诉它本端已经完成了数据的发送,但允许继续接收来自对方的数据,直到对方也发送结束报文段以关闭连接。TCP连接的这种状态称为半关闭(halfclose)状态,如下图所示。

连接超时
前面我们讨论的是很快建立连接的情况。如果客户端访问一个距离它很远的服务器,或者由于网络繁忙,导致服务器对于客户端发送出的同步报文段没有应答,此时客户端程序将产生什么样的行为呢?显然,对于提供可靠服务的TCP来说,它必然是先进行重连(可能执行多次),如果重连仍然无效,则通知应用程序连接超时。
TCP状态转移
TCP状态转移过程

服务器端和客户端状态转移过程

服务器端
-
连接
服务器通过listen系统调用,进入LISTEN状态,当接收到同步报文,进入SYN_RCVD状态(已接收同步报文状态),发送SYN和ACK,当接收到ACK,转移到ESTABLISHED状态。
-
被动关闭
当收到结束报文,服务器返回ACK并进入CLOSE_WAIT状态(已接收结束报文状态),等待服务器应用程序关闭连续,关闭时也会给客户端发送结束报文,进入LAST_ACK状态,等待ACK,一旦确认,连接彻底关闭。
客户端
-
连接
客户端主动发送同步报文进入SYN_SENT(已发送同步报文状态),如果接收到ACK,进入ESTABLISHED状态,并返回ACK。这一过程通过connect系统调用完成,调用失败主要有以下原因
- 如果connect连接的目标端口不存在(未被任何进程监听),或者该端口仍被处于TIME_WAIT 状态的连接所占用(见后文),则服务器将给客户端发送一个复位报文,段, connect调用失败。
- 如果目标端口存在,但connect在超时时间内未收到服务器的确认报文段,则connect调用失败。
-
主动关闭
当客户端执行主动关闭时,它将向服务器发送一个结束报文段,同时连接进人FIN_WAIT_1状态(已发送结束报文状态1)。
-
半关闭状态
- (半关闭状态)若此时客户端收到服务器专门用于确认目的的确认报文段(比如图3-6中的TCP报文段5),则连接转移至FIN_WAIT_2状态(已发送报文状态2)。当客户端处于FIN_WAIT_2状态时,服务器处于CLOSE_WAIT状态,这一对状态是可能发生半关闭的状态(能继续接收数据)。
- 此时如果服务器也关闭连接(发送结束报文段),则客户端将给予确认并进人TIME_WAIT状态。
TIME_WAIT状态
客户端连接在收到服务器的结束报文段之后,并没有直接进人CLOSED状态,而是转移到TIME_WAIT状态。在这个状态,客户端连接要等待一段长为2MSL (Maximum Segment Life,报文段最大生存时间)的时间,才能完全关闭。MSL是TCP报文段在网络中的最大生存时间,标准文档RFC 1122的建议值是2min。
TIME WAIT状态存在的原因有两点:
-
可靠地终止TCP连接。
假设用于确认服务器结束报文段6的TCP报文段7丢失,那么服务器将重发结束报文段。因此客户端需要停留在某个状态以处理重复收到的结束报文段(即向服务器发送确认报文段)。否则,客户端将以复位报文段来回应服务器,服务器则认为这是一个错误,因为它期望的是确认报文段。
-
保证让迟来的TCP报文段有足够的时间被识别并丢弃。
如果马上进入cloed状态,该端口又被建立新的连接,可能会收到原来连接的数据,2MSL保证了两个传输方向上的TCP报文段已经消失。
复位报文段(非正常连接处理方式)
在某些特殊条件下,TCP连接的一端会向另一端发送携带RST标志的报文段,即复位报文段,以通知对方关闭连接或重新建立连接。本节讨论产生复位报文段的3种情况。
- 访问不存在的端口,主机将返回复位报文段。
- 异常终止连接
- 服务器(或客户端)关闭或者异常终止了连接,而对方没有接收到结束报文段(比如发生了网络故障),此时,客户端(或服务器)还维持着原来的连接,而服务器(或客户端)即使重启,也已经没有该连接的任何信息了。我们将这种状态称为半打开状态,处于这种状态的连接称为半打开连接。如果客户端(或服务器)往处于半打开状态的连接写入数据,则对方将回应一个复位报文段。
TCP超时重传
TCP服务必须能够重传超时时间内未收到确认的TCP报文段。为此,TCP模块为每个TCP报文段都维护一个重传定时器,该定时器在TCP报文段第一次被发送时启动。如果超时时间内未收到接收方的应答, TCP模块将重传TCP报文段并重置定时器。至于下次重传的超时时间如何选择,以及最多执行多少次重传,就是TCP的重传策略。
流量控制(滑动窗口)(针对接收者缓存区)
为什么要有滑动窗口?
- TCP 是每发送一个数据,都要进行一次确认应答,你一句我一句。但这种方式的缺点是数据包的往返时间越长,通信的效率就越低。
- TCP 引入了窗口这个概念,窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。
窗口大小由哪一方决定?
TCP 头字段叫窗口大小的信息,这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。
发送滑动窗口

接收滑动窗口

流量控制
发送方不能无脑的发数据给接收方,要考虑接收方处理能力。如果一直无脑的发数据给对方,但对方处理不过来,那么就会导致触发重发机制,从而导致网络流量的无端的浪费。为了解决这种现象发生,TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。
拥塞控制(针对网络流量)
TCP模块还有一个重要的任务,就是提高网络利用率,降低丢包率,并保证网络资源对每条数据流的公平性。这就是所谓的拥塞控制
为什么要有拥塞控制呀,不是有流量控制了吗?
前面的流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络的中发生了什么。一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大。
什么是拥塞窗口?和发送窗口有什么关系呢?
我们在前面提到过发送窗口 swnd 和接收窗口 rwnd 是约等于的关系,那么由于加入了拥塞窗口的概念后,此时发送窗口的值是swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值。
拥塞窗口 cwnd 变化的规则:
- 只要网络中没有出现拥塞,cwnd 就会增大;
- 但网络中出现了拥塞,cwnd 就减少
那么怎么知道当前网络是否出现了拥塞呢?
其实只要「发送方」没有在规定时间内接收到 ACK 应答报文,也就是发生了超时重传,就会认为网络出现了拥塞。
慢启动
慢启动的算法记住一个规则就行:当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1。
那慢启动涨到什么时候是个头呢?
有一个叫慢启动门限 ssthresh (slow start threshold)状态变量。
- 当 cwnd < ssthresh 时,使用慢启动算法。
- 当 cwnd >= ssthresh 时,就会使用「拥塞避免算法」
拥塞避免
前面说道,当拥塞窗口 cwnd 「超过」慢启动门限 ssthresh 就会进入拥塞避免算法。一般来说 ssthresh 的大小是 65535 字节。
那么进入拥塞避免算法后,它的规则是:每当收到一个 ACK 时,cwnd 增加 1/cwnd。拥塞避免算法就是将原本慢启动算法的指数增长变成了线性增长,还是增长阶段,但是增长速度缓慢了一些。
就这么一直增长着后,网络就会慢慢进入了拥塞的状况了,于是就会出现丢包现象,这时就需要对丢失的数据包进行重传。当触发了重传机制,也就进入了「拥塞发生算法」。
拥塞发送
当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:
-
超时重传
这个时候,ssthresh 和 cwnd 的值会发生变化:
- ssthresh 设为 cwnd/2
- cwnd 重置为 1 (是恢复为 cwnd 初始化值,我这里假定 cwnd 初始化值 1),接着,就重新开始慢启动.但是这种方式太激进了,反应也很强烈,会造成网络卡顿。
-
快速重传
- cwnd = cwnd/2 ,也就是设置为原来的一半;
- ssthresh = cwnd;
快速恢复
快速重传和快速恢复算法一般同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。
然后,在快速重传之后,进入快速恢复算法如下:
- 拥塞窗口
cwnd = ssthresh + 3( 3 的意思是确认有 3 个数据包被收到了); - 重传丢失的数据包;
- 如果再收到重复的 ACK,那么 cwnd 增加 1;
- 如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态;

浙公网安备 33010602011771号