# 拥塞窗口 · 在途数据别太多

> 除了控制发送速率，还要限制「已发出但还没被确认」的在途数据量——这是拥塞窗口。这一章读 goog_cc 的 congestion_window_pushback_controller，讲清在途数据为什么要有上限、超限时怎么「回压」减速，以及它和 pacing、码率控制如何配合防止缓冲区膨胀。

- 作者：David（道雾轩）
- 专栏：音视频与 WebRTC 详解（https://daiw.org/manual/webrtc.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/webrtc/congestion-window
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# 拥塞窗口 · 在途数据别太多

[Pacing 控制“发多快”](https://daiw.org/manual/webrtc/pacing)，但还有一个正交的约束：**“已经发出去、但还没收到[确认](https://daiw.org/manual/webrtc/transport-cc)”的数据（in-flight）不能太多。** 这个约束叫**拥塞窗口（congestion window）**，由 `goog_cc/congestion_window_pushback_controller.h` 实现。它是 GoogCC 防止延迟失控的又一道闸。

## 在途数据：堆在网络里的量

<Callout type="info">
  **“在途数据”= 你已经发出、但对端还没[通过反馈确认收到](https://daiw.org/manual/webrtc/transport-cc)的那些包的总量。** 它反映“此刻有多少数据正堆在网络的缓冲区里”。哪怕[发送速率](https://daiw.org/manual/webrtc/pacing)看着合理，如果在途数据**持续堆积**（发得比确认回来的快），这些数据就在瓶颈路由器的缓冲区里**排长队**——这正是 **bufferbloat（缓冲区膨胀）** 的成因：缓冲区被塞满，每个包都要等前面一长串，[延迟飙升](https://daiw.org/manual/webrtc/transport-cc)。

  拥塞窗口给在途数据**设一个上限**：一旦在途量逼近上限，就**别再往里灌**了，等确认回来、腾出空间再发。这和 [TCP 的拥塞窗口 cwnd](https://daiw.org/manual/webrtc/delay-based-bwe) 是同一个思想——**不光管“速率”，还管“总量”**，双重保险地防止把网络缓冲区塞爆。
</Callout>

## Pushback：超限就回压

WebRTC 的实现叫 `CongestionWindowPushbackController`——名字里的 **pushback（回压）** 点明了它的工作方式：

- 计算一个**目标拥塞窗口**（基于估计的[带宽](https://daiw.org/manual/webrtc/delay-based-bwe)和 [RTT](https://daiw.org/manual/webrtc/connection-and-checks)：带宽 × RTT ≈ “填满这条路”所需的数据量）。
- 监控当前在途数据量。
- **在途量超过窗口** → **回压**：主动**压低**实际发送码率（在 pacing 之上再踩一脚刹车），让发送慢下来，直到在途数据回落到窗口内。

<Callout type="info">
  **“回压”是速率控制之外的一道额外刹车，专治延迟失控。** 正常时，[延迟based](https://daiw.org/manual/webrtc/delay-based-bwe) / [丢包based](https://daiw.org/manual/webrtc/loss-based-bwe)估出的码率 + [pacing](https://daiw.org/manual/webrtc/pacing) 已经在管发送。但某些情况下（如网络突然变差、[反馈延迟](https://daiw.org/manual/webrtc/transport-cc)），在途数据可能来不及被限制住而堆积——拥塞窗口作为**最后一道闸**，一旦发现在途量超标就强行回压减速。它优先保护的是**延迟**：宁可暂时少发点、画质降一点，也不让缓冲区堆积导致通话[延迟暴涨、卡顿](https://daiw.org/manual/webrtc/transport-cc)。对实时通信，**低延迟比高画质更重要**——拥塞窗口把这个优先级落到了实处。
</Callout>

## 三道闸，层层设防

到这里，GoogCC 控制发送的手段已经有三层，各管一个维度：

| 机制 | 管什么 | 类比 |
| --- | --- | --- |
| [码率估计](https://daiw.org/manual/webrtc/delay-based-bwe)（delay/loss） | **平均该发多快** | 定速度上限 |
| [Pacing](https://daiw.org/manual/webrtc/pacing) | **发包的瞬时节奏**（别突发） | 匀速放行 |
| **拥塞窗口** | **在途总量**（别堆积） | 限制“路上的车” |

三者协同：码率估计定“目标速度”，pacing 保证“匀速吐出”，拥塞窗口兜底“总量不超”。**多重、正交的约束，让 GoogCC 在各种刁钻网络下都能稳住延迟**——这种“不把鸡蛋放一个篮子”的冗余设计，是实时系统鲁棒性的来源。

## 小结与下一章

**拥塞窗口**限制**在途（已发未[确认](https://daiw.org/manual/webrtc/transport-cc)）数据量**，防 **bufferbloat**（缓冲区膨胀导致[延迟飙升](https://daiw.org/manual/webrtc/transport-cc)）。`CongestionWindowPushbackController` 算一个目标窗口（≈带宽 × [RTT](https://daiw.org/manual/webrtc/connection-and-checks)），在途超限就**回压**——在 [pacing](https://daiw.org/manual/webrtc/pacing) 之上再踩刹车，优先保**低延迟**。它是[码率估计](https://daiw.org/manual/webrtc/delay-based-bwe)、[pacing](https://daiw.org/manual/webrtc/pacing) 之外的**第三道闸**，三者正交设防。

估计、探测、pacing、窗口都齐了。最后一章把它们收成一个**闭环**，并看估出的码率怎么变成[编码器](https://daiw.org/manual/webrtc/source-map)的指令。👉 [码率控制与闭环](https://daiw.org/manual/webrtc/rate-control)。

## 参考资料

- [1] libwebrtc 源码.`modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h`。
- [2] 关联 bufferbloat 与 TCP cwnd —— 在途数据限制的共同思想。
- [3] IETF「GoogCC」draft。
