3、TCP/IP协议栈回顾:TCP三次握手与四次挥手、滑动窗口、拥塞控制

做带宽测试和瓶颈分析,TCP协议栈是你绕不开的核心。说白了,网络上的大部分流量都是TCP承载的。你测出来的带宽好不好,很多时候不是线路的问题,而是TCP协议栈在背后“搞事情”。

我个人习惯,在分析任何性能问题之前,先回顾一下TCP的几个关键机制。这就像修车之前得先看发动机原理图,不然你连故障灯亮在哪都看不懂。

3.1 TCP三次握手:连接建立的“敲门砖”

三次握手,大家面试都背过。但你真的理解它对带宽测试的影响吗?

流程很简单:

  1. 客户端发SYN:我想跟你说话,这是我的初始序列号(ISN)。
  2. 服务端回SYN+ACK:收到,这是我的ISN,确认你的序列号。
  3. 客户端发ACK:收到确认,咱们开始聊。

嗯,这里要注意。三次握手的开销,在短连接场景下非常致命。我在项目中遇到过,一个HTTP请求只有几KB,但握手就占了两个RTT(往返时间)。你想想看,如果RTT是100ms,光握手就200ms没了,带宽再高也白搭。

核心影响: 三次握手引入了至少1.5个RTT的延迟。对于带宽测试工具(比如 iperf),如果使用短连接模式,测出来的结果会远低于实际可用带宽,因为大量时间浪费在握手上了。

我的建议: 做带宽测试时,尽量使用长连接(比如 iperf 的默认模式),或者使用 TCP_NODELAY 选项禁用 Nagle 算法,避免握手阶段的数据包被延迟合并。

3.2 四次挥手:优雅地“说再见”

四次挥手比握手复杂一点,因为它涉及半关闭状态。流程是:

  1. 主动方发FIN:我说完了。
  2. 被动方回ACK:知道了,但我还有话没说完。
  3. 被动方发FIN:我也说完了。
  4. 主动方回ACK:收到,拜拜。

这里有个坑,叫 TIME_WAIT 状态。主动关闭连接的一方,在发送最后一个ACK后,会进入TIME_WAIT,等待2MSL(最大报文生存时间)后才彻底关闭。

我曾经在压测服务器时,发现端口被占满了,新连接连不上。查了半天,就是TIME_WAIT太多。每个连接关闭后,端口要等60秒才能复用。高并发场景下,这简直是灾难。

避坑指南: 我曾经在压测时,因为没调优TIME_WAIT,导致端口耗尽,测试直接中断。解决方案:调整内核参数 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_tw_recycle(注意:tcp_tw_recycle 在NAT环境下有坑,慎用),或者使用 SO_LINGER 选项强制关闭。

3.3 滑动窗口:流量控制的“水龙头”

滑动窗口,说白了就是接收方告诉发送方:“你一次别发太多,我缓冲区就这么大。” 它解决了发送方和接收方处理速度不匹配的问题。

窗口大小决定了在未收到ACK确认前,最多能发送多少数据。窗口越大,吞吐量越高,但风险也越大——万一丢包了,重传的数据量也大。

我习惯用 ss -ti 命令查看当前连接的窗口大小。比如:

# 查看当前TCP连接的窗口信息
ss -ti | grep -E "cwnd|ssthresh|rtt"

# 输出示例
cwnd:20 ssthresh:14 rtt:10ms

这里 cwnd 是拥塞窗口,ssthresh 是慢启动阈值。窗口大小 = min(接收窗口, 拥塞窗口)。

实战经验: 我在调优一个跨国专线时,发现带宽利用率只有30%。查了接收窗口,发现默认只有64KB。而RTT是200ms,BDP(带宽延迟积)算下来需要1MB的窗口才能跑满。调大 net.core.rmem_max 和 net.ipv4.tcp_rmem 后,带宽直接拉满。

3.4 拥塞控制:网络的“交通警察”

拥塞控制是TCP最核心、也最复杂的部分。它防止发送方把网络“堵死”。主要算法有四个阶段:

阶段 行为 触发条件
慢启动 cwnd 从1开始,每收到一个ACK翻倍 连接刚建立,或超时重传后
拥塞避免 cwnd 线性增长(每RTT加1) cwnd 达到 ssthresh
快速重传 收到3个重复ACK,立即重传丢失的包 检测到丢包
快速恢复 ssthresh 减半,cwnd 设为 ssthresh + 3 快速重传后

你想想看,如果网络有1%的丢包率,TCP的拥塞控制会频繁触发快速重传和快速恢复,cwnd 不断减半。带宽利用率能高才怪。

不同的拥塞控制算法,对带宽测试结果影响巨大。比如:

  • CUBIC:Linux默认算法,适合高带宽长肥网络。
  • BBR:Google开发的,基于带宽和RTT建模,不依赖丢包。我在跨国链路上用BBR,带宽利用率从40%提升到了90%。
  • Reno:老算法,丢包后窗口减半,性能较差。

切换算法: 你可以通过 sysctl net.ipv4.tcp_congestion_control 查看当前算法,用 sysctl -w net.ipv4.tcp_congestion_control=bbr 切换。注意,需要内核支持。

3.5 知识体系总览

下面这张图,是我梳理的TCP协议栈核心逻辑。它把三次握手、滑动窗口、拥塞控制串在了一起。你看完应该能明白,为什么带宽测试结果会受这么多因素影响。

TCP协议栈核心逻辑总览 三次握手 建立连接 SYN → SYN+ACK → ACK 滑动窗口 流量控制 接收窗口 vs 拥塞窗口 拥塞控制 慢启动/拥塞避免 快速重传/快速恢复 握手细节 • 客户端 → SYN (seq=x) • 服务端 → SYN+ACK (seq=y, ack=x+1) • 客户端 → ACK (seq=x+1, ack=y+1) • 影响:增加1.5 RTT延迟 窗口细节 • 接收窗口(rwnd):接收方缓冲区 • 拥塞窗口(cwnd):发送方控制 • 实际窗口 = min(rwnd, cwnd) • 调优:BDP = 带宽 × RTT 拥塞控制细节 • 慢启动:cwnd指数增长 • 拥塞避免:cwnd线性增长 • 快速重传:3个重复ACK触发 • 算法:CUBIC/BBR/Reno 带宽测试关键影响 • 短连接:三次握手消耗大量RTT,建议使用长连接测试 • 窗口大小:必须大于BDP,否则带宽无法跑满 • 拥塞控制:丢包环境下,BBR算法比CUBIC更优

这张图把三个核心机制串起来了。你从左边看到右边,就是一次TCP数据传输的完整生命周期。握手建立连接,滑动窗口控制流量,拥塞控制应对网络变化。任何一个环节出问题,带宽测试结果都会失真。

总结一句话: 做带宽测试前,先检查TCP参数。调优窗口和拥塞控制算法,往往比换硬件更有效。我见过太多人花大价钱升级带宽,结果问题出在服务器默认的TCP配置上。

想要更多资料,联系可可