bigywater.tech
网络协议 · 53

拥塞控制 2:Reno、CUBIC、BBR 的行为差异

Reno、CUBIC、BBR 到底有什么不同?本机用的是哪一个,它的窗口增长真的符合公式吗?这篇验证 CUBIC 的三次曲线,用 Mathis 公式对比实测吞吐,再用一个明确标注的玩具模拟比较基于丢包和基于测量的两类思路。

6%
实测 cwnd 与 CUBIC 公式预测值的相对误差中位数(13 个点)
2.73 倍
0.3% 丢包、往返 100 ms 时,实测吞吐是 Reno(Mathis 公式)预测的倍数(0.1% 时 2.10 倍,3.0% 时 1.04 倍)
0.7
CUBIC 的乘性减小系数 β(Reno 是 0.5),在 w52 里已经测出

哪个算法

实测 · 本机执行输出
  net.inet.tcp.cubic_sockets: 407
  net.inet.tcp.use_newreno: 0
  net.inet.tcp.pacing: 0
  net.inet.tcp.cc_debug: 0
  use_newreno = 0 表示默认不用 NewReno;cubic_sockets 是当前在用 CUBIC 的套接字数(随时间变化);没有找到按套接字切换算法的 setsockopt(Linux 的 TCP_CONGESTION 在 macOS 上没有对应项),所以下面不做 Reno / BBR 的实测对照

本机默认是 CUBIC;macOS 上没有按套接字选择算法的接口,所以没有 Reno / BBR 的实测对照。

CUBIC 的曲线

实测 · 本机执行输出
  传输 36.6 MB;找到 5 个快速恢复事件(降到降前的 60%~80%)
    事件时刻   降前 W_max(MSS)  降后(MSS)   K(秒)   丢包后 0.3 s:实测/预测   0.6 s:实测/预测   0.9 s:实测/预测(MSS)
      0.51 s           33          23      2.91   24/26                 26/28                 27/30       
      2.22 s           33          24      2.91   26/26                 (中间又丢包)               (中间又丢包)     
      2.72 s           26          19      2.69   20/21                 21/22                 23/24       
      3.73 s           24          17      2.62   18/19                 20/21                 21/22       
      7.15 s           43          30      3.18   31/33                 29/36                 25/38       
  13 个可比较的点上,实测与预测的相对误差:中位 6%,最大 35%——量级吻合;不完全相等是因为 ① 实测 cwnd 受 ACK 到达节奏和 10 ms 采样影响;② 窗口很小时 CUBIC 会切换到"像 Reno 一样增长"的 TCP 友好区域;③ 公式里的 C、β 是 RFC 8312 的建议值,实现可能有差别

丢包后窗口按 C·(t − K)³ + W_max 回升,先快后平再快;实测与公式基本吻合。

与 Reno 对比

实测 · 本机执行输出
    丢包率    实测吞吐(MB/s)    Reno(Mathis 公式)(MB/s)   实测 / 公式
      0.1%           13.25                      6.30                 2.10
      0.3%            9.92                      3.64                 2.73
      1.0%            3.88                      1.99                 1.95
      3.0%            1.19                      1.15                 1.04
  丢包越少,CUBIC 比 Reno 公式的预测快得越多(高 BDP 路径上 CUBIC 恢复更快);丢包很多时两者接近。公式是粗略的稳态估计,这里只看趋势

用 Mathis 公式当作 Reno 的预期吞吐,实测的 CUBIC 在高时延路径上快 1~2.7 倍。

BBR 的思路

实测 · 本机执行输出
    丢包率     Reno(MB/s)   CUBIC(MB/s)   BBR 近似(MB/s)   链路带宽 12.5 MB/s
      0.01%        1.74         3.25          11.87
      0.10%        0.57         0.56          11.86
      1.00%        0.19         0.10          11.75
      3.00%        0.10         0.04          11.52
  这个模拟只体现思路:基于丢包的算法(Reno / CUBIC)把每个丢包都当成拥塞,随机丢包多的链路(无线、长距离)吞吐被压得很低;BBR 依据带宽和 RTT 的测量建模,对随机丢包不敏感——但 BBR 的真实表现(与别的流公平、队列长度)模拟里没有体现,本机也没法切换到 BBR 实测

玩具模拟说明:基于丢包的算法在随机丢包多的链路上吞吐被压得很低,基于带宽 / RTT 测量的算法不受影响。BBR 一栏是假设设定,不是实测。

分步演示

拥塞控制算法
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64(Apple 芯片)/ Python 3.14.8 / tcpdump(libpcap)/ scapy 2.8 / dpkt / dnspython / h2 / aioquic / OpenSSL / curl 8.7.1;抓包在 lo0(环回)上做,少数实验用真实网卡 en0(这台机器上 en0 是 Wi-Fi,tcpdump 看到的是系统转成以太网格式的帧,所以文中的“以太网头”都指这个 14 字节的头)或访问公网(会在文章里注明);需要丢包 / 延迟 / 限速时,用一个只作用于 lo0 端口 9400-9499 的 dummynet 辅助脚本(netlab);所有服务端都是本机自己起的进程;时间戳取自 tcpdump 的 -tt,数字每次运行会有小幅抖动。第二、三节在 lo0 上,往返 100 ms 和随机丢包由 netlab 注入;cwnd 来自 TCP_CONNECTION_INFO 的 10 ms 采样,丢包随机,每次运行的曲线不同;CUBIC 公式的 C = 0.4、β = 0.7 取自 RFC 8312;第四节是玩具模拟,其中的 BBR 一栏是按假设直接设定的,不是模拟结果
    • 没有验证:Reno 和 BBR 的实测(本机不能按套接字切换);CUBIC 在 TCP 友好区域的行为;BBR 的带宽 / RTT 估计、公平性与排队;ECN 对拥塞控制的影响;更长时间尺度下的稳态吞吐
    京ICP备2026067502号-1京公网安备11010502063487号