TCP拥塞控制算法
约 1760 字大约 6 分钟
tcpcongestion
2025-07-01
概述
TCP拥塞控制是互联网能够稳定运行的核心机制之一。它通过动态调整发送速率来避免网络拥塞,在"尽可能快"与"不造成拥塞"之间寻找平衡。本文将深入解析各种拥塞控制算法的原理与演进。
核心概念
- cwnd(Congestion Window): 拥塞窗口,发送方维护,限制在收到ACK前可发送的数据量
- rwnd(Receiver Window): 接收窗口,接收方通告,表示接收方的缓冲区剩余空间
- ssthresh(Slow Start Threshold): 慢启动阈值,区分慢启动和拥塞避免阶段
- MSS(Maximum Segment Size): 最大报文段长度
- RTT(Round-Trip Time): 往返时延
- 实际发送窗口 = min(cwnd, rwnd)
经典拥塞控制四阶段
1. 慢启动(Slow Start)
连接建立后,cwnd从1个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,转入拥塞避免2. 拥塞避免(Congestion Avoidance)
当cwnd达到ssthresh后,转为线性增长:每个RTT内cwnd增加1个MSS(即每收到一个ACK,cwnd增加MSS/cwnd个MSS)。
3. 快速重传(Fast Retransmit)
当发送方连续收到3个重复ACK时,立即重传丢失的报文段,而不等待超时定时器触发。
4. 快速恢复(Fast Recovery)
在快速重传后,ssthresh = cwnd/2,cwnd = ssthresh + 3 MSS,然后进入拥塞避免阶段。
cwnd变化曲线
cwnd
(MSS)
^
| /\
| / \ /\
| / \ / \ /
| / \ / \ /
| / \ / \ /
| / \ / \ /
| / X \/
| /
| /
|/
+----------------------------------------→ 时间
|← 慢启动 →|← 拥塞避免 →|← 快恢复 →|TCP Reno
TCP Reno实现了完整的四阶段拥塞控制,但存在一个缺陷:当一个窗口内有多个丢包时,每个丢包都会导致cwnd减半,性能急剧下降。
丢包处理:
- 超时:ssthresh = cwnd/2, cwnd = 1 MSS(回到慢启动)
- 3个重复ACK:ssthresh = cwnd/2, cwnd = ssthresh + 3(快速恢复)TCP NewReno
NewReno改进了Reno的快速恢复阶段,通过"部分确认"(Partial ACK)机制处理同一窗口内的多个丢包。
TCP CUBIC
CUBIC是Linux自2.6.19以来的默认拥塞控制算法。它使用三次函数(cubic function)来调整cwnd,使窗口增长独立于RTT。
核心公式:
W(t) = C × (t - K)³ + Wmax
其中:
- C: 缩放常数(通常为0.4)
- t: 自上次丢包以来的时间
- K: W(t)从W_max/β增长到W_max所需的时间
- Wmax: 上次丢包时的窗口大小
- β: 乘法减小因子(通常为0.7)cwnd
^
| ___________
| / \
| Wmax ............/.............\..............
| / \
| / \
| / \
| / \
| Wmax×β / \
| | |
+-------+----------------------------+------→ 时间
丢包点 丢包点CUBIC的特点:
- 窗口增长与RTT无关,对长肥管道(高带宽高延迟)友好
- 在Wmax附近增长缓慢(凸部分),远离Wmax时增长快速(凹部分)
- 乘法减小因子β=0.7(Reno为0.5),丢包后窗口保留更多
TCP BBR
BBR(Bottleneck Bandwidth and Round-trip propagation time)是Google于2016年提出的全新拥塞控制算法,从根本上改变了思路——不以丢包为拥塞信号,而是通过测量带宽和延迟来决定发送速率。
核心思想:
BBR状态机:
- Startup: 类似慢启动,指数增长,直到检测到带宽不再增长
- Drain: 排空Startup阶段多余的数据包
- ProbeBW: 稳态阶段,周期性地以1.25×、0.75×、1×的速率探测带宽
- ProbeRTT: 周期性降低inflight以测量最小RTT
启用BBR:
# 检查当前拥塞控制算法
sysctl net.ipv4.tcp_congestion_control
# 启用BBR
echo "net.core.default_qdisc = fq" >> /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control = bbr" >> /etc/sysctl.conf
sysctl -p
# 验证
sysctl net.ipv4.tcp_congestion_control
# 输出: net.ipv4.tcp_congestion_control = bbr算法对比
| 特性 | Reno | NewReno | CUBIC | BBR |
|---|---|---|---|---|
| 拥塞信号 | 丢包 | 丢包 | 丢包 | 带宽/延迟 |
| 增长方式 | 线性(AIMD) | 线性(AIMD) | 三次函数 | 基于模型 |
| RTT公平性 | 差 | 差 | 好 | 好 |
| 浅缓冲适应 | 差 | 差 | 一般 | 好 |
| 深缓冲适应 | 一般 | 一般 | 一般 | 好 |
| 高丢包率 | 差 | 一般 | 一般 | 好 |
| 实现复杂度 | 低 | 中 | 中 | 高 |
Loss-Based vs Delay-Based
ECN(Explicit Congestion Notification)
ECN是一种不依赖丢包就能通知拥塞的机制。路由器在缓冲区即将溢出时,在IP头部设置ECN标记,接收端通过TCP头部的ECE标志通知发送端。
IP头部 TOS字段最后2位:
- 00: 不支持ECN
- 01/10: 支持ECN(ECT)
- 11: 拥塞已发生(CE)
TCP头部标志位:
- ECE (ECN-Echo): 接收端通知发送端发生拥塞
- CWR (Congestion Window Reduced): 发送端确认已减小窗口# 启用ECN
sysctl -w net.ipv4.tcp_ecn=1
# 值说明:
# 0 = 禁用
# 1 = 请求使用ECN(客户端/服务端)
# 2 = 仅服务端响应ECN请求实际调优建议
# 高带宽低延迟场景(数据中心内)
net.ipv4.tcp_congestion_control = cubic
net.ipv4.tcp_ecn = 1
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# 高带宽高延迟场景(跨洲际链路)
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
net.ipv4.tcp_rmem = 4096 87380 67108864
net.ipv4.tcp_wmem = 4096 65536 67108864
# 高丢包场景(无线/移动网络)
net.ipv4.tcp_congestion_control = bbr总结
TCP拥塞控制算法经历了从简单的AIMD(Reno)到RTT无关的CUBIC,再到基于模型的BBR的演进。每种算法适用于不同场景:CUBIC在一般网络环境表现均衡,BBR在高延迟和高丢包场景优势明显。选择合适的拥塞控制算法并配合内核参数调优,可以显著提升网络吞吐量。
贡献者
更新日志
9f6c2-feat: organize wiki content and refresh site setup于