TCP 拥塞控制
网络不是无限大的管道。TCP 如何感知网络拥塞并优雅降速?
1. 什么是拥塞?为什么要控制?
拥塞:太多数据注入网络,超过了路由器/链路的处理能力,导致丢包和延迟增加。
TCP 的拥塞控制回答两个关键问题:
- 如何发现拥塞? → 丢包(超时或重复 ACK)是主要信号
- 发现后怎么做? → 降低发送速率,再慢慢恢复
RFC 5681 定义了四个经典拥塞控制算法,RFC 9293 引用这些算法作为 TCP 拥塞控制的基础。
2. 核心概念:拥塞窗口(cwnd)
发送方维护一个拥塞窗口(cwnd),限制已发送但未确认的数据量:
实际可发送量 = min(cwnd, rwnd)
cwnd 以字节为单位,初始值通常为 1-10 MSS。cwnd 动态变化,反映网络状况。
3. 四个经典算法
3.1 慢启动(Slow Start)
名称有误导性——其实并不慢,cwnd 呈指数增长。
触发时机:连接刚建立 或 超时重传发生后。
规则:
- cwnd 初始值 = IW(Initial Window,通常 1-10 MSS)
- 每收到一个 ACK,cwnd += 1 MSS
- 每个 RTT,cwnd 翻倍(指数增长)
RTT 1: cwnd = 1 MSS → 发 1 个段
RTT 2: cwnd = 2 MSS → 发 2 个段
RTT 3: cwnd = 4 MSS → 发 4 个段
RTT 4: cwnd = 8 MSS → 发 8 个段
...
何时停止?
- cwnd ≥ ssthresh(慢启动阈值)→ 进入拥塞避免
- 发生丢包 → cwnd 减半(或重置),进入恢复
3.2 拥塞避免(Congestion Avoidance)
cwnd 超过 ssthresh 后,增速放缓为线性增长。
规则:每个 RTT cwnd += 1 MSS(大约)。实际实现中常用:每收到 ACK,cwnd += MSS × MSS / cwnd。
RTT 5: cwnd = 9 MSS
RTT 6: cwnd = 10 MSS
RTT 7: cwnd = 11 MSS
...
3.3 快速重传(Fast Retransmit)
不需要等 RTO 超时。连续收到 3 个重复 ACK(即 4 个相同 ACK),发送方立即重传丢失的段。
发送方发了 seq: 1 2 3 4 5
↓ X ↓ ↓ ↓
接收方收到: 1 3 4 5
ack=2 ack=2 ack=2 ack=2 (重复 ACK)
↑
第 4 个相同 ACK → 快速重传 seq=2
为什么是 3 个?因为少量重复 ACK 可能只是乱序而非丢包,3 个是经验和统计的平衡。
3.4 快速恢复(Fast Recovery)
快速重传后不会退回慢启动(那太慢了),而是执行快速恢复:
- ssthresh = cwnd / 2
- cwnd = ssthresh + 3 × MSS(补偿已经离开网络的 3 个段)
- 每收到一个重复 ACK,cwnd += 1 MSS(保持数据流动)
- 收到新 ACK 后,cwnd = ssthresh,进入拥塞避免
4. 算法状态转换图
连接建立 / 超时重传
↓
┌──────────┐
│ 慢启动 │
└────┬─────┘
cwnd≥ssthresh
↓
┌──────────┐
┌────→│ 拥塞避免 │←──────────┐
│ └────┬─────┘ │
│ 3 dup ACK 收到新ACK │
│ ↓ │
│ ┌──────────┐ │
│ │ 快速重传 │ │
│ └────┬─────┘ │
│ ↓ │
│ ┌──────────┐ │
└────│ 快速恢复 │─────────────┘
└──────────┘
5. BBR 简介
BBR(Bottleneck Bandwidth and Round-trip propagation time)是 Google 在 2016 年提出的新一代拥塞控制算法,已纳入 Linux 内核。
与传统算法的区别
| 维度 | 传统(CUBIC/Reno) | BBR |
|---|---|---|
| 拥塞信号 | 丢包(被动反应) | 带宽+延迟(主动探测) |
| 窗口调整 | 基于丢包 AIMD | 基于 BDP 模型 |
| 缓冲区 | 倾向于填满(bufferbloat) | 尽量不填满 |
| 适用场景 | 一般网络 | 高延迟、有一定丢包的网络 |
BBR 的核心思想:不再用丢包作为唯一拥塞信号,而是持续测量瓶颈带宽和最小 RTT,计算出 BDP(带宽延迟积),让 inflight 数据保持在 BDP 附近,既不浪费带宽也不填满缓冲区。
6. 小结
TCP 拥塞控制经历了数十年演进:
- Tahoe(1988):慢启动 + 拥塞避免 + 快速重传
- Reno(1990):加入快速恢复
- NewReno(1999):改进快速恢复
- CUBIC(2008):Linux 默认,改线性为三次函数
- BBR(2016+):基于模型而非丢包
拥塞控制是 TCP 性能的灵魂。理解了它,你就能理解为什么网络时快时慢,以及如何调优。
本篇要点
- 拥塞控制保护网络,流量控制保护接收方;实际发送量 = min(cwnd, rwnd)
- 慢启动:cwnd 指数增长(每个 RTT 翻倍),名字有误导性,其实增长很快
- ssthresh 是慢启动阈值:cwnd < ssthresh 走慢启动(指数),≥ ssthresh 走拥塞避免(线性)
- 快速重传:收到 3 个重复 ACK 立即重传丢失段,不必等 RTO 超时
- 快速恢复:重传后 cwnd 减半而非归零,直接进入拥塞避免(不回慢启动)
- 超时重传 vs 快速重传:超时说明网络严重拥塞(cwnd 重置+慢启动),快速重传说明轻微丢包(cwnd 减半+快速恢复)
- BBR 不依赖丢包信号,通过测量瓶颈带宽和最小 RTT 计算最优发送速率,解决 bufferbloat 问题