TCP流量控制与连接管理详解
一、流量控制
1. 核心思想与目标
-
思想 :让发送方 的发送速率不要太快,要让接收方来得及接收。
-
目标 :消除发送方使接收方缓冲区溢出的可能性 。这是一个速度匹配服务,是接收方主导的,针对的是接收方的处理能力。
2. 实现机制:滑动窗口协议
TCP通过让发送方维护一个称为接收窗口的变量来提供流量控制,这个窗口动态地反映了接收方剩余的缓冲区空间。
-
接收窗口 :接收方在每次发送ACK时,会通过TCP首部的 "窗口大小" 字段,告知发送方自己当前还有多少可用的缓冲区空间。
-
发送窗口:发送方必须保证,已发送但未被确认的数据量不能超过接收方通告的接收窗口大小。
3. 工作流程详解
下图直观地展示了TCP流量控制中接收窗口如何动态地控制发送方的行为:

4. 特殊问题:零窗口死锁与持续计时器
-
问题:如果接收方通告的窗口大小为0,发送方会停止发送数据。之后,当接收方缓冲区空出,它会发送一个包含新窗口大小的ACK。如果这个ACK丢失,双方将陷入死锁:接收方等待数据,发送方等待窗口更新。
-
解决方案 :TCP规范要求,发送方在收到零窗口通告后,启动一个持续计时器 。当计时器超时,发送方会发送一个零窗口探测报文段(1字节数据),以触发接收方重新发送其窗口大小。如果此时窗口仍然为0,则重置持续计时器;如果窗口已打开,则数据传输恢复。
二、TCP连接管理
TCP是面向连接的协议,这意味着在数据交换之前,通信双方必须相互建立连接,在通信结束后要断开连接。
1. 连接建立:三次握手
这是确保双方都知道彼此愿意并且能够进行通信的关键步骤。
步骤详解:
-
SYN :客户端发送一个特殊的TCP报文段。该报文段不包含应用层数据,但其首部中的SYN比特被置为1 。同时,客户端随机选择一个初始序列号,并将其放在序列号字段中。
-
SYN-ACK :服务器收到SYN报文段后,会为该连接分配TCP缓冲区和变量,并发送一个允许连接的报文段。这个报文段包含:SYN比特置为1 ,ACK比特置为1 ,其确认号字段 为
client_isn + 1,同时服务器也随机选择一个自己的初始序列号。 -
ACK :客户端收到SYN-ACK后,也要为该连接分配缓冲区和变量。然后,客户端向服务器发送另一个报文段,对服务器的允许连接进行确认:SYN比特置为0 ,ACK比特置为1 ,确认号字段 为
server_isn + 1。
为什么是三次,而不是两次?
核心是解决"延迟的重复连接请求"问题。
假设只有两次握手:一个旧的、延迟的SYN包到达服务器,服务器会认为客户端请求新连接并回复SYN-ACK。如果只有两次,连接就此建立,但客户端可能根本没有想建立连接,导致服务器资源浪费。
而三次握手 下,服务器在收到延迟的SYN并回复SYN-ACK后,客户端会忽略这个它未曾请求的SYN-ACK,不会发送ACK,因此服务器最终不会建立连接,避免了资源浪费。
2. 连接终止:四次挥手
由于TCP连接是全双工的(数据可以双向独立流动),每个方向必须单独关闭。
步骤详解:
-
FIN:客户端应用进程发出关闭连接命令,客户端TCP发送一个FIN报文段(FIN比特置为1)。
-
ACK :服务器收到FIN后,回复一个ACK报文段,确认这个FIN。此时,从客户端到服务器的连接就关闭了。TCP连接处于半关闭状态:服务器仍然可以发送数据给客户端。
-
FIN:当服务器也准备关闭连接时,其应用进程发出关闭命令,服务器TCP发送它自己的FIN报文段(FIN比特置为1)。
-
ACK :客户端收到服务器的FIN后,回复一个ACK报文段。此时,客户端会进入一个等待状态 (通常是2倍的最大报文段寿命,2MSL),以确保这个ACK能到达服务器。之后,双方的所有资源都被释放。
为什么需要TIME_WAIT状态?
-
可靠地终止连接:确保最后的ACK能够到达服务器。如果ACK丢失,服务器会超时重传FIN,处于TIME_WAIT的客户端可以重发ACK。
-
让旧的重复报文段在网络中消逝:防止之前连接中延迟的报文段被误认为是新连接的报文,造成数据混乱。
总结
| 机制 | 核心目标 | 关键手段 |
|---|---|---|
| 流量控制 | 匹配速度 ,防止接收方缓冲区溢出。 | 接收窗口,通过ACK动态通告,控制发送方窗口。 |
| 连接建立 | 同步序列号 ,确保双方准备好通信。 | 三次握手(SYN, SYN-ACK, ACK)。 |
| 连接终止 | 安全地、可靠地关闭双向通道。 | 四次挥手(FIN, ACK, FIN, ACK)及TIME_WAIT状态。 |
流量控制和连接管理共同确保了TCP连接能够有序地建立、高效稳定地运行、以及安全彻底地关闭,构成了TCP服务质量的坚实基础。
TCP连接终止:四次挥手与"三次挥手"的真相
一、标准的四次挥手
首先,我们必须理解,TCP连接是全双工的,这意味着数据在两个方向上可以独立传输。因此,关闭一个连接需要分别关闭两个方向的数据流。
步骤解析:
-
主动关闭方(客户端)发送 FIN:表示我没有数据要发送了。
-
被动关闭方(服务器)发送 ACK:表示"我收到你的FIN了"。
-
此时,从客户端到服务器的数据通道关闭。
-
但服务器到客户端的数据通道仍然可用! 服务器可能还有数据要发送给客户端。
-
-
被动关闭方(服务器)发送 FIN:当服务器也没有数据要发送时,它发送自己的FIN。
-
主动关闭方(客户端)发送 ACK:客户端确认服务器的FIN。
-
此时,从服务器到客户端的数据通道也关闭了。
-
客户端进入
TIME_WAIT状态。
-

二、所谓的"三次挥手"
"三次挥手"发生在一种特殊场景下:当被动关闭方(服务器)在收到客户端的FIN后,也没有任何数据要发送了。
在这种情况下:
-
客户端发送 FIN。
-
服务器在回复ACK时,可以将自己的FIN和ACK合并在一起发送。
-
也就是说,第二次挥手和第三次挥手合并了。
-
服务器发送的是一个
[FIN + ACK]报文段。
-
这样,整个流程就从4个报文段减少到了3个:
text
客户端 服务器
| FIN |
| ----------> |
| |
| [FIN+ACK] |
| <---------- |
| |
| ACK |
| ----------> |
三、为什么合并是可行的?
因为TCP报文段首部有多个控制比特(Flags),它们可以同时被置为1。在合并的报文段中:
-
ACK比特 = 1 ,且 确认号字段 有效(确认了客户端的FIN)。
-
FIN比特 = 1 ,且 序列号字段 有效(表示服务器自己的数据流结束)。
接收方(客户端)能够正确解析这个报文段,知道它既是对自己FIN的确认,也是对方发起的连接关闭请求。
四、总结与类比
-
四次挥手是根本协议:它定义了关闭一个全双工连接所需的两个独立的半关闭操作。
-
"三次挥手"是一种优化:当被动关闭方没有待发送数据时,通过将第二个ACK和第三个FIN合并为一个报文,提高了效率,减少了网络通信次数。
-
这类似于"捎带确认":就像有时数据报文段可以捎带对对方数据的确认一样,这里的FIN报文段捎带了对对方FIN的确认。
结论:您记得的"三次挥手"是真实存在的,但它并非一个独立的协议,而是TCP协议栈在满足特定条件时,对标准四次挥手流程的一种合法且高效的优化实现。理解四次挥手的本质,就能轻松理解"三次挥手"为何会发生。