犀牛鸟 2026 研究笔记 estelledc.github.io

第 5 章:拥塞控制——在黑暗中找到最佳车速

从高速公路说起

你在高速公路上开车。路况你看不太清(前面是一片浓雾),但你知道几件事:

  1. 开太快 → 追尾(网络丢包)
  2. 开太慢 → 浪费时间(没用满带宽)
  3. 你能感知到的信号:前车的尾灯(ACK 延迟)、刹车灯(丢包通知)、路上的车流密度(RTT 变化)

拥塞控制就是在这种”看不清前方”的情况下,找到那个最佳车速——既不丢包,又能充分利用带宽。

而且更难的是:路上不只你一辆车。所有的车(所有的连接)都在用同样的策略试探,需要大家”公平”地共享道路容量。


为什么 QUIC 的拥塞控制更难?

你可能会想:TCP 的拥塞控制已经研究了 40 年,QUIC 直接搬过来不就行了?

答案是:没那么简单。

挑战一:看不到中间信息

TCP 的头部是明文的,路由器可以设置 ECN(显式拥塞通知)标志位告诉发送端”我快满了”。QUIC 的头部大部分加密了,中间设备帮不上忙。

TCP:路由器能帮忙
  路由器发现队列快满了 → 设置 ECN 标志 → TCP 收到后减速
  相当于:路边有电子限速牌告诉你"前方拥堵,请减速"

QUIC:只能自己判断
  中间设备看不到 QUIC 的内部 → 无法主动通知
  只能靠:丢包、RTT 变化、ACK 延迟来推断
  相当于:浓雾中没有限速牌,只能靠自己感觉
  
  注:QUIC 支持 ECN,但需要端到端验证,且很多路径不支持

挑战二:用户态实现的精度问题

TCP 的拥塞控制在内核中实现,有纳秒级的时间戳精度。QUIC 在用户态,受到操作系统调度的影响:

内核态(TCP):
  收到 ACK → 立即处理 → 微秒级响应
  
用户态(QUIC):
  收到 ACK → 内核唤醒进程 → 上下文切换 → 处理
  延迟可能多 10-100 微秒
  
  对 BBR 这种依赖精确时间测量的算法,这个误差不可忽略

挑战三:多流复用的公平性

一个 QUIC 连接里有多个流。拥塞控制是在连接级别做(所有流共享一个拥塞窗口),还是在流级别做(每个流独立控制)?

连接级拥塞控制(当前标准做法):
  所有流共享一个拥塞窗口
  优点:一个连接不会抢占过多带宽(对其他TCP连接公平)
  缺点:重要的流可能被不重要的流拖慢

流级拥塞控制(理论研究):
  每个流独立控制
  优点:可以优先保证重要流
  缺点:一个连接开100个流,等于抢了100份带宽(对其他连接不公平)
  
  当前所有六大实现都采用连接级拥塞控制

拥塞控制基础概念

在讲具体算法之前,先统一几个核心概念:

拥塞窗口(CWND)

拥塞窗口 = 发送端在收到确认之前最多能发多少字节

直觉:
  CWND = 10KB → 最多10KB的数据在路上"飞"着还没被确认
  CWND 越大 → 发送速率越高
  
  发送速率 ≈ CWND / RTT
  
  例:CWND=100KB, RTT=100ms
  发送速率 ≈ 100KB / 0.1s = 1000KB/s = 1MB/s

带宽延迟积(BDP)

BDP = 带宽 × RTT = 管道里能装多少数据

日常类比:
  想象一条水管,水管的直径就是带宽,长度就是RTT
  BDP = 水管的容积 = 同时"在路上"的数据量
  
  例:10Mbps 带宽,100ms RTT
  BDP = 10Mbps × 0.1s = 1Mb = 125KB
  
  意味着:拥塞窗口设为125KB时,刚好能"装满"这条管道
  - CWND < BDP → 管道没装满,浪费带宽
  - CWND > BDP → 管道溢出,数据在路由器排队,增加延迟
  - CWND >> BDP → 路由器队列满了,开始丢包

慢启动(Slow Start)

所有拥塞控制算法都有一个”探索阶段”:

慢启动过程:
  1. 初始 CWND = 10个包(约14KB)
  2. 每收到一个 ACK → CWND 翻倍
  3. 指数增长:10 → 20 → 40 → 80 → 160...
  4. 直到发生丢包或达到某个阈值
  
  名字叫"慢启动"但其实增长很快(指数级)
  从0到填满管道大约需要 log2(BDP/initial_cwnd) 个RTT
  
  例:BDP=125KB, initial=14KB
  14 → 28 → 56 → 112 → 224(超了!) 
  大约4个RTT就能填满管道

CUBIC:基于丢包的经典算法

核心思想

CUBIC 是目前 Linux/Windows/macOS 的默认 TCP 拥塞控制算法。它的核心思想是:

1. 持续增大发送速率
2. 直到发生丢包
3. 丢包时记住当前窗口大小(Wmax)
4. 将窗口减小到 Wmax × 0.7
5. 用一个三次方(cubic)函数逐渐恢复到 Wmax
6. 超过 Wmax 后缓慢探索更高窗口

窗口变化曲线(像一个被压扁的S):
  
  CWND
   ^
   |         Wmax ←── 丢包点
   |        / ·····→ 缓慢探索新高
   |      /
   |    /  ← cubic曲线(先快后慢再快)
   |  /
   | / 
   |/ ← 丢包后窗口减到 0.7×Wmax
   +──────────────────────────────> 时间

CUBIC 的优势和局限

优势:
  - 简单、稳定、经过20年验证
  - 对大多数网络条件表现不错
  - 公平性好(多个CUBIC流能均分带宽)

局限:
  - "丢包驱动":必须等丢包才减速,意味着总会触发丢包
  - 在缓冲区大的链路上(bufferbloat)会造成高延迟
  - 在高带宽高延迟链路上收敛慢(需要很多RTT才能填满管道)
  - 不适合频繁变化的移动网络

代码示例:CUBIC 在 TQUIC 中的实现

// TQUIC 中 CUBIC 的核心逻辑(简化)
impl CongestionController for Cubic {
    fn on_ack(&mut self, bytes_acked: usize, rtt: Duration) {
        if self.in_slow_start() {
            // 慢启动:每个ACK增加一个MSS
            self.cwnd += bytes_acked;           // ← 指数增长
        } else {
            // 拥塞避免:使用cubic函数
            let t = now() - self.epoch_start;   // ← 距离上次丢包的时间
            let w_cubic = self.c * (t - self.k).powi(3) + self.w_max;
            self.cwnd = w_cubic as usize;       // ← cubic曲线更新
        }
    }
    
    fn on_loss(&mut self) {
        self.w_max = self.cwnd;                 // ← 记住丢包时的窗口
        self.cwnd = (self.cwnd as f64 * 0.7) as usize;  // ← 减到70%
        self.epoch_start = now();               // ← 开始新的cubic周期
        self.k = ((self.w_max - self.cwnd) as f64 / self.c).cbrt();
    }
}

BBR:基于带宽估测的革命性算法

为什么需要 BBR?

CUBIC 有一个根本性的问题:它通过”制造丢包”来探测带宽上限。但在现代网络中,路由器有很大的缓冲区(buffer)。CUBIC 会把这些 buffer 填满,导致所有经过这个路由器的连接延迟都变高——这就是bufferbloat(缓冲区膨胀)。

CUBIC 的问题:bufferbloat

路由器的缓冲区 = 排队等待的数据包
  
  理想状态:缓冲区空的,包立即转发,延迟低
  CUBIC 工作时:不断增加速率直到丢包
    → 缓冲区被填满之后才丢包
    → 在缓冲区满之前,延迟已经很高了!
  
  延迟(RTT)
   ^
   |              xxxxxxxxxx ← buffer满了,开始丢包
   |          xxxx           ← 延迟越来越高(buffer在填)
   |      xxxx
   |  xxxx ← 理想延迟(管道满但buffer空)
   | x
   +────────────────────────> 发送速率
       ↑                ↑
   管道满但        buffer满
   buffer空        开始丢包
   (最佳点)       (CUBIC在这里才减速)

BBR 的思路完全不同:不等丢包,而是主动测量带宽和最小 RTT,找到那个”管道满但 buffer 空”的最佳工作点。

BBR 的核心模型

BBR 全称 Bottleneck Bandwidth and Round-trip propagation time。它维护两个关键估计值:

BtlBw (Bottleneck Bandwidth):瓶颈带宽
  = 最近一段时间观测到的最大交付速率
  = "这条路最宽处能通多少车"

RTprop (Round-trip Propagation time):最小传播延迟
  = 最近一段时间观测到的最小RTT
  = "路上没车时走一趟要多久"

目标发送速率 = BtlBw(填满管道)
目标 inflight = BtlBw × RTprop = BDP(不超过管道容量)

BBR 的四个阶段

BBR 状态机:

┌──────────────────────────────────────────────────┐
│                                                  │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐   │
│  │ STARTUP  │───>│  DRAIN   │───>│PROBE_BW  │◄─┐│
│  └──────────┘    └──────────┘    └──────────┘  ││
│                                       │        ││
│                                       ▼        ││
│                                  ┌──────────┐  ││
│                                  │PROBE_RTT │──┘│
│                                  └──────────┘   │
└──────────────────────────────────────────────────┘

STARTUP (慢启动):
  - 指数增长探测带宽
  - 直到交付速率不再增长(管道满了)
  
DRAIN (排空):
  - 把 STARTUP 阶段积累的队列排空
  - 降低发送速率到 BDP
  
PROBE_BW (带宽探测,主要状态):
  - 周期性上下浮动发送速率(±25%)
  - 探测带宽是否增加了
  - 大部分时间在这个状态
  
PROBE_RTT (延迟探测):
  - 每 10 秒左右,短暂降低 inflight 到 4 个包
  - 排空路由器队列,测量真实最小 RTT
  - 持续约 200ms

BBR vs CUBIC:行为对比

场景:100Mbps链路,100ms RTT,路由器buffer可容纳200ms的数据

CUBIC 行为:
  1. 慢启动,速率指数增长
  2. buffer 被填满后丢包
  3. 此时 RTT 已经从 100ms 涨到了 300ms(100ms传播 + 200ms排队)
  4. 减速到 70%,然后 cubic 恢复
  5. 周而复始:RTT 在 100-300ms 之间波动

BBR 行为:
  1. STARTUP 阶段快速找到 BtlBw = 100Mbps
  2. DRAIN 阶段排空多余队列
  3. PROBE_BW 阶段保持发送速率在 100Mbps 左右
  4. RTT 保持在 100-110ms(几乎不排队)
  
  结果:同样的带宽利用率,BBR 的延迟低得多

BBR2/BBR3:BBR 的进化

BBR 的问题

BBR v1 有几个已知问题:

  1. 与 CUBIC 共存不公平:BBR 倾向于抢占更多带宽
  2. 在高丢包率下过于激进:不理会丢包,一直按估测带宽发送
  3. 对实际RTT的反应不够灵敏

BBR2 的改进

BBR2(也称 BBRv2)增加了:

BBR2 关键改进:
  1. 丢包响应:如果丢包率超过阈值,降低发送速率
     (BBR1 几乎忽略丢包)
  
  2. ProbeRTT 改进:更温和的 RTT 探测
     (BBR1 会突然把 inflight 降到 4 个包,造成吞吐量暴跌)
  
  3. 与 CUBIC 的公平性:
     BBR1: 可能占 CUBIC 的 2-3 倍带宽
     BBR2: 大致公平
  
  4. 更好的 buffer 适应:
     根据 buffer 大小动态调整行为

BBR3:TQUIC 的独特实现

BBR3 是 BBR 系列的最新版本,目前只在 Linux 内核的实验分支中有实现。TQUIC 是唯一在 QUIC 用户态实现了 BBR3 的项目

BBR3 进一步改进:
  1. 更精确的带宽探测周期
  2. 改进的 inflight 上限控制
  3. 更好的 ECN 支持
  4. 对瞬态网络变化更鲁棒

TQUIC 的 BBR3 实现位置:
  src/congestion_control/bbr3.rs
  
  这对犀牛鸟参赛者是一个重要的贡献点:
  - BBR3 的行为验证和测试
  - 与其他算法的公平性测试
  - 针对移动网络场景的优化

COPA:延迟敏感的算法

核心思想

COPA(Competitive Online Policy Adjustment)的目标是最小化延迟,同时保持合理的吞吐量。它特别适合实时应用(视频通话、游戏)。

COPA 的核心公式(直觉版):

  目标速率 = 1 / (δ × RTT_standing)
  
  其中:
  - δ 是一个调节参数(越大越保守)
  - RTT_standing 是近期观测到的"稳定RTT"
  
  直觉:
  - RTT 增高 → 速率降低(因为队列在积累)
  - RTT 降低 → 速率增加(队列在排空)
  
  和 BBR 的区别:
  - BBR 试图"填满管道但不填buffer"
  - COPA 更保守:积极地把延迟控制在最低

适用场景:
  - 视频通话(腾讯会议、微信视频):延迟 > 吞吐量
  - 实时游戏:需要稳定的低延迟
  - 不适合:大文件下载(会浪费带宽)

TQUIC 实现 COPA 的原因很明确:腾讯的业务场景中有大量实时通信需求(微信视频通话、腾讯会议、QQ 视频等)。


各实现的拥塞控制支持对比

┌───────────┬────────┬─────┬──────┬──────┬──────┬──────────┐
│ 算法       │ quiche │ quinn│ s2n  │ TQUIC│ msquic│ ngtcp2  │
├───────────┼────────┼─────┼──────┼──────┼──────┼──────────┤
│ CUBIC     │   ✓    │  ✓  │  ✓   │  ✓   │  ✓   │   ✓     │
│ BBR       │   ✓*   │  ✗  │  ✗   │  ✓   │  ✗   │   ✓     │
│ BBR2      │   ✓    │  ✗  │  ✗   │  ✗   │  ✗   │   ✗     │
│ BBR3      │   ✗    │  ✗  │  ✗   │  ✓   │  ✗   │   ✗     │
│ COPA      │   ✗    │  ✗  │  ✗   │  ✓   │  ✗   │   ✗     │
│ NewReno   │   ✓    │  ✓  │  ✗   │  ✗   │  ✓   │   ✓     │
│ Hystart++ │   ✓    │  ✓  │  ✓   │  ✓   │  ✓   │   ✓     │
└───────────┴────────┴─────┴──────┴──────┴──────┴──────────┘

* quiche 的 BBR 是从 Chromium 移植的

关键观察:
  - TQUIC 在拥塞控制算法多样性上是最丰富的(5种)
  - BBR3 和 COPA 是 TQUIC 独有的
  - quiche 的特色是有 Chromium 移植的 BBR 实现(经过大规模验证)

丢包检测:拥塞控制的”眼睛”

拥塞控制需要知道”什么时候发生了丢包”。QUIC 的丢包检测比 TCP 更精确:

QUIC 的丢包判定规则(RFC 9002)

规则1:包号阈值(Packet Threshold)
  如果一个包之后有 3 个更高编号的包被确认了,
  但这个包还没被确认 → 判定丢失
  
  例:发送了包 100-110
       收到 ACK 确认了 101, 102, 103
       包 100 还没被确认 → 丢了(3个之后的包已确认)

规则2:时间阈值(Time Threshold)
  如果一个包发送后超过 max(smoothed_rtt, latest_rtt) × 9/8 的时间
  还没被确认 → 判定丢失
  
  例:smoothed_rtt = 100ms
       阈值 = 100ms × 9/8 = 112.5ms
       包 100 发送 113ms 后还没被确认 → 丢了

为什么比 TCP 好?
  TCP 重传歧义:SEQ 可复用,ACK 可能确认的是原始包或重传包
  QUIC 无歧义:包号单调递增,每个 ACK 明确对应唯一的包

PTO(Probe Timeout):探测超时

当没有任何 ACK 回来时(可能所有包都丢了):

PTO 计算:
  PTO = smoothed_rtt + max(4 × rttvar, 1ms) + max_ack_delay
  
  例:srtt=100ms, rttvar=25ms, max_ack_delay=25ms
  PTO = 100 + max(100, 1) + 25 = 225ms

PTO 触发时:
  发送 1-2 个探测包(不等于判定丢失!)
  如果探测包的 ACK 回来了 → 之前的包可能只是延迟了
  如果探测包也超时 → PTO翻倍,继续探测

这比 TCP 的 RTO(重传超时)更灵活:
  TCP RTO:超时 → 直接判定丢失 → 窗口暴降
  QUIC PTO:超时 → 先探测 → 确认了再判定

quiche 的双引擎设计

quiche 有一个独特的设计:它同时维护了两套拥塞控制实现

quiche 拥塞控制架构:

┌─────────────────────────────────────────┐
│           quiche 连接层                   │
├─────────────────────────────────────────┤
│     CongestionControl trait              │
├──────────────────┬──────────────────────┤
│  Native 实现      │  Chromium 移植        │
│  - CUBIC          │  - BBR (从Chrome移植) │
│  - NewReno        │  - BBR2              │
│  - Hystart++      │                      │
│                   │  保持与Chrome一致的    │
│  纯Rust实现       │  行为(包括bug!)      │
└──────────────────┴──────────────────────┘

为什么两套?
  1. Native: Rust 风格,干净的抽象,适合阅读和修改
  2. Chromium: 经过亿级用户验证的生产实现
     → Cloudflare 实际使用 Chromium 版本
     → 但维护负担大(要跟踪 Chromium 的更新)

s2n-quic 的 PathPublisher:可观测性设计

s2n-quic 在拥塞控制方面的特色不是算法种类多,而是可观测性

// s2n-quic 的 PathPublisher 设计(简化)
pub trait PathPublisher {
    fn on_congestion(&self, event: CongestionEvent);
    fn on_recovery(&self, event: RecoveryEvent);
    fn on_rtt_update(&self, event: RttEvent);
    fn on_pacing(&self, event: PacingEvent);
}

// 使用示例:
struct MetricsCollector;
impl PathPublisher for MetricsCollector {
    fn on_congestion(&self, event: CongestionEvent) {
        // 记录拥塞事件到 metrics 系统
        // 包括:时间、窗口大小、丢包数、恢复方式
        metrics::counter!("quic.congestion.events").increment(1);
    }
}

// 好处:生产环境中能实时观察拥塞控制的行为
// AWS 大规模部署时这种可观测性是必须的

TQUIC 的拥塞控制架构

TQUIC 是拥塞控制算法最丰富的实现。它的设计体现了”给每种业务场景最好的算法”:

TQUIC 拥塞控制选择逻辑:

业务场景               推荐算法        原因
────────────────────────────────────────────────────
大文件下载/CDN         CUBIC          成熟稳定,公平性好
视频流                 BBR/BBR3       高吞吐,低延迟
视频通话              COPA           最低延迟
弱网/移动网络         BBR3           对变化反应快
需要与TCP公平共存     CUBIC          行为最接近TCP
// TQUIC 中创建拥塞控制器(简化)
pub fn new_congestion_controller(
    algorithm: CongestionControlAlgorithm,
    config: &Config,
) -> Box<dyn CongestionController> {
    match algorithm {
        CongestionControlAlgorithm::Cubic => {
            Box::new(Cubic::new(config))
        }
        CongestionControlAlgorithm::Bbr => {
            Box::new(Bbr::new(config))
        }
        CongestionControlAlgorithm::Bbr3 => {
            Box::new(Bbr3::new(config))      // ← 独有
        }
        CongestionControlAlgorithm::Copa => {
            Box::new(Copa::new(config))      // ← 独有
        }
    }
}

TQUIC 的每路径拥塞控制

在多路径模式下,TQUIC 为每条路径维护独立的拥塞控制器:

多路径拥塞控制:

Path 0 (WiFi):
  RTT: 20ms, BW: 50Mbps
  拥塞控制器: BBR3
  CWND: 独立计算
  
Path 1 (4G):
  RTT: 80ms, BW: 20Mbps
  拥塞控制器: BBR3(独立实例)
  CWND: 独立计算

为什么每条路径要独立?
  - WiFi 和 4G 的网络特征完全不同
  - 用同一个拥塞控制器无法适应两条路径
  - 一条路径丢包不应该影响另一条路径的发送速率

这是第6章(多路径QUIC)的前置知识

Pacing:发送节奏控制

除了拥塞窗口(CWND)决定”能发多少”,pacing 决定”什么时候发”:

没有 pacing(突发模式):
  CWND = 100 个包
  所有100个包在一个瞬间全部发出 → 路由器瞬间收到100个包
  可能立即触发队列溢出和丢包
  
  |                                    
  |  |||||||||||||||||||||||  ← 突发!
  |                          ← 空闲
  |  |||||||||||||||||||||||  ← 又一个突发
  +──────────────────────────> 时间

有 pacing(平滑模式):
  同样的 CWND = 100 个包
  但均匀分布在一个 RTT 内发送
  
  |  | | | | | | | | | | | | | | | | | | |  ← 平滑!
  +──────────────────────────> 时间
  
  发送间隔 = RTT / CWND(每个包之间等一小段时间)

pacing 对 BBR 尤其重要——BBR 的设计假设发送是平滑的。没有 pacing 的 BBR 会产生严重的测量误差。

各实现的 pacing 支持:

实现 Pacing 方式
quiche 计算 pacing rate,由应用层负责实际调度
quinn 集成 tokio 定时器
s2n-quic Pacing Provider trait
TQUIC 内置 pacing 调度器
msquic 内核层级的精确调度
ngtcp2 提供 pacing 时间戳,由应用负责

算法选择指南

你的场景 ──────────── 推荐算法 ──── 原因
    │
    ├── 大文件下载/CDN ── CUBIC ──── 最成熟,与TCP公平
    │
    ├── 视频流/直播 ──── BBR/BBR3 ── 高带宽利用,低延迟
    │
    ├── 视频通话 ──────── COPA ───── 最低延迟
    │
    ├── 弱网环境 ──────── BBR3 ───── 对网络变化反应快
    │
    ├── 高丢包率 ──────── CUBIC ──── BBR在高丢包下过于激进
    │
    └── 不确定 ────────── CUBIC ──── 安全的默认选择


更精确的决策矩阵:

            低延迟需求     中延迟需求     不关心延迟
低丢包      COPA/BBR3     BBR/BBR3      CUBIC
中丢包      COPA          BBR3          CUBIC  
高丢包      COPA          CUBIC         CUBIC

拥塞控制的未来趋势

机器学习驱动的拥塞控制

传统方法:手工设计规则(if 丢包 then 减速)
ML方法:让模型学习最优策略

已有的研究:
  - Orca (SIGCOMM 2022):用强化学习做拥塞控制
  - PCC (NSDI 2018):基于在线学习的速率控制
  
  挑战:
  - 训练环境和真实网络的差异
  - 公平性难以保证
  - 可解释性差(出问题时不知道为什么)
  
  目前还没有任何 QUIC 实现集成了 ML 拥塞控制
  → 这是一个潜在的研究方向

多路径感知的拥塞控制

当前:每条路径独立做拥塞控制
未来:路径之间协同决策

例:WiFi 突然丢包严重
  当前做法:WiFi 路径独立减速
  协同做法:检测到WiFi不佳 → 主动把更多流量转移到4G
  
  TQUIC 的 MultipathScheduler 已经在做类似的事
  → 调度器根据各路径状态决定数据分配
  → 但和拥塞控制的集成还在探索中

本章小结

算法 核心信号 优势 劣势 适用场景
CUBIC 丢包 成熟稳定公平 bufferbloat 通用/下载
BBR 带宽+RTT 低延迟高吞吐 与CUBIC不公平 视频流
BBR2 带宽+RTT+丢包 修复BBR问题 仍在演进 通用
BBR3 改进的BBR2 更精确更鲁棒 新/验证少 移动网络
COPA RTT 最低延迟 吞吐量低 实时通信

一句话总结:拥塞控制是在”利用带宽”和”避免排队”之间走钢丝。CUBIC 靠丢包才转向,BBR 靠测量提前判断,COPA 为了低延迟放弃部分带宽。TQUIC 给你所有选择。


读完本章你能做什么

  1. 解释 CUBIC 和 BBR 的核心区别(丢包驱动 vs 带宽测量)
  2. 说出 BBR 的四个状态及其作用
  3. 解释为什么 QUIC 的拥塞控制比 TCP 难实现
  4. 根据应用场景选择合适的拥塞控制算法
  5. 说出 TQUIC 在拥塞控制方面的独特贡献(BBR3、COPA、每路径控制)
  6. 解释 pacing 的作用

常见误区

误区一:BBR 总是比 CUBIC 好

正确理解:BBR 在”有路由器大缓冲”的场景下延迟更低,但在”缓冲很小 + 高丢包”的场景下可能表现更差(BBR v1 几乎忽略丢包)。而且 BBR 与 CUBIC 共存时会抢占更多带宽——这对其他用户不公平。没有”总是更好”的拥塞控制算法,只有”更适合当前场景”的算法。

误区二:QUIC 一定比 TCP 有更好的拥塞控制

正确理解:QUIC 的优势不在于算法更好(用的是同样的 CUBIC/BBR),而在于:1. 更精确的 RTT 测量(包号不复用);2. 更灵活的部署(可以随 App 更新);3. 可以针对业务定制。同一个算法在 TCP 和 QUIC 上的表现差异不大——差异来自测量精度和部署灵活性。

误区三:拥塞窗口越大越好

正确理解:拥塞窗口超过 BDP 后,多出来的数据会在路由器缓冲区排队。排队 = 延迟增加。CWND 的最优值是恰好等于 BDP:管道满了但没有排队。超过这个值就是在”用延迟换吞吐量”——对下载可以接受,对实时通信不可接受。


导读目录:README.md 上一章:第 4 章 · 连接建立与握手——从陌生人到老朋友 下一章:第 6 章 · 多路径 QUIC——同时走两条路