05. TCP 传输层状态机与拥塞控制全景
💡 交互图解
本章理论对应的动态微观仿真位于:👉 CS 10 图全屏走查 · FIG 06 状态机 与 FIG 07 滑动窗口与拥塞水流波形
1. 三次握手与状态跃迁全景
为什么必须是三次握手?不能是两次?
防历史旧连接死灰复燃: 设想客户端发出的第一个 SYN 在某个网络路由器节点被严重拥堵挂起(超时),客户端放弃并重新发送第二个 SYN 顺利完成通信后关闭了连接。 很久之后,那个在网络中游荡的旧 SYN 突然抵达服务端。若只有两次握手,服务端一收到旧 SYN 就误认为新连接已经建立,分配内存与 socket,并白白傻等客户端发送数据。而三次握手下,客户端收到不对劲的 ACK 时会发出 RST 复位包终结该连接。
2. 四次挥手与 TIME_WAIT 2MSL
为什么必须在客户端保留 TIME_WAIT 并等待 2MSL?
- 保证第四次挥手的 ACK 能可靠到达服务端:若最后的 ACK 丢失,服务端因收不到确认会超时重发第三次挥手的 FIN;处于 TIME_WAIT 状态的客户端能够捕捉到这个重发的 FIN,并再次补发 ACK;
- 彻底消灭网络残存报文:一个报文在网络中的最大生存时间为 MSL(Maximum Segment Lifetime,通常为 30s~60s)。2MSL 时间足够让本连接产生的所有方向的游魂报文在互联网中自然消亡,防止老报文串入后续新建立的具有完全相同四元组(源IP/端口、目的IP/端口)的新连接中。
3. 滑动窗口与流量控制(Flow Control)
TCP 是以字节为单位的滑动窗口协议。双方各有一个缓冲区:
- 发送窗口
: rwnd(Receive Window):由接收方在 TCP 首部告知,防止接收方应用层处理不及时导致缓冲区溢出;cwnd(Congestion Window):由发送方根据网络拥塞程度自适应推演。
4. 拥塞控制四大算法波形(Reno 体系)
5. 生产级进阶:TIME_WAIT 端口耗尽与现代 BBR
生产灾难:高并发短连接导致端口枯竭
微服务调用如果没有正确使用 HTTP 连接池(每次请求都新建连接并关闭),主动关闭端会在本地累积数万个处于 TIME_WAIT 的 Socket,消耗完操作系统的临时可用端口(Ephemeral Ports 范围通常只有几万个),导致后续请求抛出 Cannot assign requested address!
- 治理之道:开启客户端 HTTP Keep-Alive;在服务间强制采用 RPC 连接池复用长连接;在 Linux 内核层评估配置
net.ipv4.tcp_tw_reuse = 1。
拥塞控制演进:从 Reno/CUBIC 到 Google BBR
传统 Reno 和 CUBIC 遵循“丢包即代表网络拥塞”的假设,在现代长肥管道(BDP 极高的高速跨国公网)或无线弱网中,少量的随机链路误码丢包会导致带宽利用率暴跌。 Google 设计的 BBR(Bottleneck Bandwidth and RTT) 通过实时测量网络传输延迟