3、TCP性能分析:窗口机制、滑动窗口与拥塞控制
TCP 性能调优,说白了就是跟“窗口”打交道。我做了这么多年网络排查,发现大部分吞吐量上不去的问题,根源都在窗口机制上。今天咱们就把这块硬骨头啃下来。
3.1 TCP 窗口机制:流量控制的基础
TCP 窗口,本质上是接收端告诉发送端“你还能发多少数据”。这个机制是为了防止发送方太快,把接收方的缓冲区撑爆。
窗口大小由接收端通告,通过 TCP 头部的 Window Size 字段传递。发送端必须遵守这个限制,不能超过对方能处理的范围。
核心公式: 实际可用窗口 = min(接收窗口, 拥塞窗口)
我遇到过不少案例,应用层吞吐上不去,抓包一看,接收窗口只有几千字节。说白了,是接收端应用没及时读取数据,导致窗口被“吃”掉了。
3.2 滑动窗口:发送端的“流水线”
滑动窗口是发送端维持的一个状态机。它把已发送但未确认的数据分成几个区域:
- 已发送并已确认 — 可以丢弃了
- 已发送但未确认 — 等待 ACK
- 未发送但可发送 — 在窗口内
- 未发送且不可发送 — 超出窗口
窗口每收到一个 ACK,就向右滑动。这样发送端可以连续发多个包,不用等一个确认再发下一个,大大提升了吞吐量。
我的经验: 调优时重点关注“窗口满”事件。如果频繁出现窗口满,说明接收端处理能力不足,或者发送端拥塞窗口太小。
3.3 拥塞控制:四阶段详解
拥塞控制是 TCP 的“刹车系统”。它防止网络中间设备(路由器、交换机)被过量数据淹没。我把它拆成四个阶段来讲。
3.3.1 慢启动
连接刚建立时,发送端不知道网络带宽有多大。所以它从很小的拥塞窗口开始,每收到一个 ACK,窗口翻倍。这是指数增长。
举个例子:初始窗口 10 个 MSS,收到 10 个 ACK 后变成 20,再下一轮变成 40。增长非常快。
注意: 慢启动并不“慢”。它只是谨慎地开始,但增长是指数级的。我曾经在 10Gbps 链路上看到慢启动在几毫秒内就把窗口撑到几千个包。
3.3.2 拥塞避免
当拥塞窗口达到慢启动阈值(ssthresh)后,TCP 进入拥塞避免阶段。增长方式从指数变为线性:每收到一个完整的窗口 ACK,窗口增加 1 个 MSS。
这个阶段是“试探性”的。发送端慢慢增加负载,直到出现丢包。
| 阶段 | 增长方式 | 触发条件 |
|---|---|---|
| 慢启动 | 指数增长(每 ACK 翻倍) | 连接建立或超时重传后 |
| 拥塞避免 | 线性增长(每窗口 +1) | cwnd >= ssthresh |
3.3.3 快速重传
当发送端收到 3 个重复的 ACK(DupACK)时,它认为某个包丢了,不等超时就直接重传。这就是快速重传。
为什么是 3 个?因为乱序也可能产生重复 ACK。1 个 DupACK 可能是乱序,2 个也可能是乱序,但 3 个基本可以确认是丢包。
避坑指南: 我曾经排查过一个案例,应用层频繁报超时,但抓包发现重复 ACK 只有 1-2 个。原因是网络存在大量乱序,触发了快速重传阈值。调整了接收端的 reorder 缓冲区后,问题解决。
3.3.4 快速恢复
快速重传后,TCP 进入快速恢复。它把 ssthresh 设为当前窗口的一半,拥塞窗口也减半,然后进入拥塞避免阶段。这样避免了直接回到慢启动,保留了部分吞吐能力。
3.4 重传与重复 ACK 分析
重传是 TCP 最核心的可靠性机制。但过多的重传意味着网络有问题。
我一般把重传分为两类:
- 超时重传(RTO) — 发送端等太久没收到 ACK,直接重传。这是最严重的,说明网络可能断连或严重拥塞。
- 快速重传 — 收到 3 个 DupACK 后触发。相对较轻,通常只是丢了一个包。
重复 ACK 本身不是坏事。它是接收端告诉发送端“我还在,但我缺了某个包”。但如果重复 ACK 数量太多,说明丢包率很高。
我的分析习惯: 抓包后先看重传率。如果超过 1%,就要深入排查。我会用 Wireshark 的“分析 → 专家信息”快速定位重传和 DupACK 的分布。
3.5 核心流程:拥塞控制状态机
下面这张图把整个拥塞控制的状态流转画清楚了。我建议你把它存下来,排查问题时对照着看。
3.6 实战排查清单
最后,我总结一个排查清单。遇到 TCP 性能问题,按这个顺序查:
- 看窗口大小 — 接收窗口是否被压到很小?如果是,检查应用层读取速度。
- 看重传率 — 超过 1% 就要警惕。用 Wireshark 的“统计 → TCP 流图”看趋势。
- 看 DupACK 数量 — 大量 DupACK 说明丢包严重,可能是网络链路问题。
- 看慢启动阶段 — 如果连接频繁回到慢启动,说明网络不稳定或拥塞。
- 看 ssthresh — 如果 ssthresh 被压得很低,说明之前发生过严重拥塞。
我曾经踩过的坑: 有一次排查数据库同步延迟,发现 TCP 窗口始终只有 16KB。查了半天,发现是中间防火墙修改了 TCP 窗口缩放因子。关闭防火墙的 TCP 优化后,窗口恢复正常,延迟从 200ms 降到 5ms。
嗯,TCP 窗口和拥塞控制这块,说白了就是“发送端想快,接收端说慢点,网络说别挤了”。理解了这三方的博弈,性能问题就解决了一半。