TCP 拥塞控制演进:从 Reno 到 BBR

前面几篇文章聚焦于 P2P 网络的架构与实现,但 P2P 应用的性能瓶颈往往不在协议层本身,而在于底层的传输协议——TCP 拥塞控制。一个 BitTorrent 节点可能同时维持数百个 TCP 连接,每个连接都在独立地进行拥塞控制。如果拥塞控制算法选择不当,P2P 节点的带宽利用率会大幅下降,尤其是在高延迟或存在随机丢包的链路上。理解拥塞控制的演进,不仅能帮助 P2P 开发者优化传输性能,也是深入理解互联网传输层运作的必经之路。

TCP 拥塞控制是互联网稳定运行的基石。自 1988 年 Van Jacobson 在 SIGCOMM 上发表经典论文以来,拥塞控制算法经历了近四十年的演进,从基于丢包的启发式方法发展到基于网络模型测量的精确控制。

拥塞控制的基本原理

所有 TCP 拥塞控制算法都围绕拥塞窗口(CWND,Congestion Window)进行工作——它表示发送端在未收到确认前可以发送的最大数据量:

1
吞吐量 ≈ min(CWND, 接收窗口) / RTT

这个公式揭示了 TCP 吞吐量的两个约束:发送端窗口(CWND)和接收端窗口(Receiver Window),两者中的较小者决定了实际吞吐量。CWND 由拥塞控制算法动态调整,而接收窗口由接收端的缓冲区大小决定。

不同算法的核心差异体现在三个维度:

  1. CWND 增长速度(慢启动 vs 拥塞避免)——决定了连接启动和带宽探测的效率
  2. 拥塞检测信号(丢包 vs 延迟 vs 带宽测量)——决定了算法对网络状态的感知方式
  3. 拥塞后的降速策略(乘法减小 vs 模型驱动的调整)——决定了拥塞事件后的恢复速度

TCP Reno(1988)

Van Jacobson 提出的经典 AIMD(Additive Increase Multiplicative Decrease)模型是拥塞控制的奠基之作。它的核心思想可以用一句话概括:轻负载时线性增长,检测到拥塞时指数退避

mermaid
flowchart TD
    subgraph 慢启动
        SS["CWND = IW (≈10 segs)<br/>每收到 ACK: CWND += 1 MSS<br/>指数增长"]
    end

    subgraph 拥塞避免
        CA["CWND ≥ ssthresh<br/>每 RTT: CWND += 1 MSS<br/>线性增长"]
    end

    subgraph 丢包处理
        LOSS["检测到 3 个重复 ACK"]
        FR["快恢复<br/>ssthresh = CWND/2<br/>CWND = ssthresh"]
        TO["超时<br/>ssthresh = CWND/2<br/>CWND = 1 MSS<br/>重新慢启动"]
    end

    SS -->|"CWND ≥ ssthresh"| CA
    CA -->|"丢包"| LOSS
    LOSS --> FR
    LOSS --> TO
    FR -->|"新 ACK"| CA
    TO --> SS

Reno 的慢启动阶段以指数方式快速探测可用带宽——每收到一个 ACK,CWND 增加一个 MSS(最大报文段长度),相当于每个 RTT 翻倍。当 CWND 达到慢启动阈值(ssthresh)后,进入拥塞避免阶段,每个 RTT 只增加 1 个 MSS,形成线性增长。一旦检测到丢包(三个重复 ACK),立即将 ssthresh 设为当前 CWND 的一半,CWND 也降到 ssthresh,进入快恢复阶段。

Reno 的局限:在高带宽高延迟链路(如跨洋光纤)上表现保守。假设一条 100ms RTT、1Gbps 带宽的链路,BDP 约为 12.5MB。如果发生丢包,CWND 减半后需要数千个 RTT 才能恢复到原窗口大小——这意味着数分钟的恢复时间,严重浪费带宽。

TCP CUBIC(2005)

CUBIC 是当前 Linux 内核的默认拥塞控制算法,由 Injong Rhee 等人提出,专门为解决 Reno 在高 BDP(带宽延迟积)网络中的低效问题而设计。

立方增长函数

CUBIC 最核心的创新是用一个三次函数来驱动窗口增长:

$$W(t) = C \times (t - K)^3 + W_{max}$$

其中 $t$ 是距离上次拥塞事件的时间,$W_{max}$ 是拥塞发生时的窗口大小,$C$ 是一个缩放常数(默认 0.4),$K = \sqrt[3]{W_{max} \times \beta / C}$ 是窗口恢复到 $W_{max}$ 所需的时间。

mermaid
flowchart TD
    A["上次拥塞事件后<br/>经过时间 t"] --> B["CUBIC 窗口函数"]
    B --> C["W(t) = C × (t - K)³ + Wmax"]
    C --> D{"窗口位置"}
    D -->|"Wmax 以下<br/>凹增长"| E["快速恢复<br/>追赶原窗口"]
    D -->|"Wmax 附近<br/>缓慢探测"| F["小心搜索<br/>避免再次拥塞"]
    D -->|"Wmax 以上<br/>凸增长"| G["积极探测<br/>新带宽上限"]
    E --> H{丢包}
    F --> H
    G --> H
    H -->|"是"| I["CWND × 0.7<br/>记录新 Wmax"]
    I --> A

CUBIC 的三大特性

CUBIC 的核心理念是用时间而非ACK 到达来驱动窗口增长,这意味着窗口增长速率与 RTT 无关:

  • 凹增长:远低于 $W_{max}$ 时快速恢复。丢包发生后,CUBIC 的窗口函数急剧上升,迅速将 CWND 拉回到拥塞前的水平——这正是 Reno 表现最差的区域。
  • 凸增长:超过 $W_{max}$ 后积极探测新高点。当 CWND 超过上次拥塞的窗口值后,函数曲线开始加速,主动寻找新的带宽上限。
  • RTT 公平性:不同 RTT 的流在固定时间内达到相同的窗口增量。在 Reno 中,短 RTT 的流拥塞窗口增长更快,容易"饿死"长 RTT 流。CUBIC 消除了这种 RTT 偏差。

为什么 CUBIC 取代了 BIC

CUBIC 的前身是 BIC(Binary Increase Congestion Control),后者使用二分搜索来探测带宽边界——这种方法在高带宽网络中表现优秀,但在低带宽网络中过于激进,且窗口函数的形状不平滑。CUBIC 用一个平滑的三次函数替代了 BIC 的分段二分搜索,既保留了高 BDP 下的效率,又改善了低带宽网络中的友好性。

BBR 的范式革命(2016)

Google 在 2016 年发布的 BBR(Bottleneck Bandwidth and Round-trip propagation time)彻底改变了拥塞控制的传统范式。核心洞察是:丢包不等同于拥塞

在无线网络、蜂窝网络等场景中,数据包可能因为信号干扰、信道衰减等原因丢失,但这并不意味着网络链路发生了拥塞。传统基于丢包的算法在这种情况下会错误地降低发送速率,造成严重的带宽浪费。

mermaid
flowchart LR
    subgraph 传统丢包基础算法
        L["Reno/CUBIC<br/>观察信号:丢包"]
        L1["假设:丢包 = 拥塞"]
        L2["行为:丢包 → 降速"]
        L --> L1 --> L2
    end

    subgraph BBR 模型基础算法
        M["BBR<br/>观察信号:带宽+RTT"]
        M1["模型:测量 BtlBw + RTprop"]
        M2["行为:在 BDP 点运行"]
        M --> M1 --> M2
    end

    L2 -.->|"在丢包链路中<br/>性能大幅下降"| CRI["对比"]
    M2 -.->|"在丢包链路中<br/>维持高吞吐"| CRI

BBR 的关键创新

  • 不依赖丢包信号:在丢包率高达 15% 的链路上仍能维持高吞吐,而 CUBIC 在 1% 丢包率时吞吐量就会大幅下降。
  • 主动测量带宽延迟积(BDP):通过测量瓶颈带宽(BtlBw)和传播延迟(RTprop),计算出链路的最佳承载数据量,在 BDP 点运行,既不产生队列也不造成浪费。
  • 避免 Bufferbloat:即使缓冲区很大,BBR 也不会填满缓冲区,从而避免大量排队延迟——这对延迟敏感的应用(如实时音视频、在线游戏)至关重要。

Linux 实操:查看与切换算法

了解了算法原理后,我们来看看如何在 Linux 系统中实际操作。以下命令可以查看和切换当前系统的拥塞控制算法:

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 查看当前使用的拥塞控制算法
sysctl net.ipv4.tcp_congestion_control

# 查看内核支持的可用算法
sysctl net.ipv4.tcp_available_congestion_control

# 临时切换到 BBR(重启后失效)
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr

# 永久生效:写入 sysctl 配置文件
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf

# 检查内核版本(BBR 需要 Linux 内核 ≥ 4.9)
uname -r

输出示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 查看当前算法
$ sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = cubic

# 查看可用算法
$ sysctl net.ipv4.tcp_available_congestion_control
net.ipv4.tcp_available_congestion_control = reno cubic bbr

# 检查内核版本(必须 ≥ 4.9 才能使用 BBR)
$ uname -r
5.15.0-91-generic

如果输出中没有 bbr,说明内核没有加载 BBR 模块。可以手动加载:

bash
1
2
3
4
5
# 加载 BBR 模块
sudo modprobe tcp_bbr

# 确认模块已加载
lsmod | grep tcp_bbr

需要注意的是,拥塞控制算法的更换是系统全局的——所有新建的 TCP 连接都会使用新算法。已经建立的连接会继续使用创建时的算法,不受切换影响。

性能对比

不同拥塞控制算法在有损链路下的表现差异巨大。以下是在相同网络条件下(100ms RTT,100Mbps 带宽)的吞吐量对比数据:

丢包率RenoCUBICBBR
0%100%100%100%
0.1%~50%~85%~99%
1%~10%~30%~95%
3%~3%~12%~88%
5%~1%~5%~80%
10%~0.5%~2%~60%

从上表可以清晰看出:

  • 在 0.1% 低丢包率下,CUBIC 还能保持约 85% 的吞吐量,Reno 已经损失了一半带宽。
  • 在 1% 丢包率下,CUBIC 跌至约 30%,而 BBR 仍能保持 95% 以上——这正是无线网络、蜂窝网络常见的丢包范围。
  • 在 5% 丢包率下,Reno 和 CUBIC 基本不可用,而 BBR 仍能维持约 80% 的吞吐量。

这也解释了为什么 P2P 应用在无线网络环境中传输性能不佳的问题根源——不仅仅是 P2P 协议的问题,底层的 TCP 拥塞控制算法同样关键。

BBR 版本演进

BBR 自发布以来经历了三个主要版本的迭代:

版本时间核心改进
BBR v12016首次提出基于模型的拥塞控制,实现高吞吐低延迟;但存在与 Reno/CUBIC 共存公平性差、STARTUP 阶段丢包率高的问题
BBR v22019引入 ECN 信号支持,改善与 Reno/CUBIC 共存时的公平性,降低 STARTUP 阶段的丢包率
BBR v32023修复带宽收敛 bug,STARTUP 增益从 2.89 降至 2.77,进一步降低排队延迟,优化短流性能

版本演进的关键驱动力

BBR 的版本演进反映了学术界和工业界对"理想拥塞控制"理解的深化:

  1. 共存公平性:BBR v1 在与其他算法共享瓶颈链路时过于激进,会抢占 CUBIC 流的带宽。v2 和 v3 通过引入更保守的竞争策略逐步改善了这个问题。
  2. STARTUP 阶段的震荡:初始版本中 STARTUP 阶段的指数增益过高(2.89),导致频繁的过度探测和丢包。v3 将增益降到 2.77,显著减少了启动震荡。
  3. 带宽收敛:在多流共享瓶颈的场景中,v1 和 v2 存在带宽分配不稳定的问题。v3 通过算法优化实现了更快、更稳定的收敛。

下一篇文章将深入 BBR 算法的内部机制和实现细节,包括其四状态机(STARTUP/DRAIN/PROBE_BW/PROBE_RTT)的完整工作流程。

参考资料

  • Jacobson, V. (1988). Congestion avoidance and control. SIGCOMM ‘88.
  • Ha, S., Rhee, I., & Xu, L. (2008). CUBIC: a new TCP-friendly high-speed TCP variant. ACM SIGOPS.
  • Cardwell, N., et al. (2016). BBR: Congestion-based congestion control. ACM Queue.
  • Cardwell, N., et al. (2022). BBR Congestion Control (IETF Draft). https://datatracker.ietf.org/doc/draft-cardwell-iccrg-bbr-congestion-control/
  • Xu, L., Harfoush, K., & Rhee, I. (2004). Binary increase congestion control (BIC) for fast long-distance networks. INFOCOM 2004.