← 返回题目列表

TCP 的拥塞控制是怎么工作的?

高频 困难 第 17 / 30 题 更新于 2026/07/28
TCP拥塞控制慢启动

简化版

拥塞控制解决「发太快压垮整个网络」的问题(流量控制是防压垮接收方,两者不同)。它靠一个拥塞窗口 cwnd动态调节发送速率,四个经典算法配合:慢启动(cwnd 指数增长探测带宽)→ 拥塞避免(到阈值后线性增长)→ 丢包时 快重传 + 快恢复(把窗口减半而不是归零)。核心思想:慢慢加速探测网络能力,一旦拥塞就退让。

详细版

发送方维护一个拥塞窗口 cwnd,实际发送窗口是 min(rwnd, cwnd)。cwnd 随网络状况动态变化,四个算法:

  1. 慢启动(Slow Start):连接初期 cwnd 从 1 个 MSS 开始,每收到一个 ACK 就 +1 个 MSS,于是每经过一个 RTT 翻倍指数增长),快速探测网络能承受多少。增长到慢启动阈值 ssthresh 就转入拥塞避免。
  2. 拥塞避免(Congestion Avoidance):cwnd 达到 ssthresh 后,改成每个 RTT 只 +1(线性增长),谨慎地继续试探,避免增长太猛引发拥塞。
  3. 快重传(Fast Retransmit):收到 3 个重复 ACK,判断某个包丢了,立即重传,不等超时。
  4. 快恢复(Fast Recovery):配合快重传——把 ssthresh 设为当前 cwnd 的一半,cwnd 也设为 ssthresh(减半而非归 1),然后进入拥塞避免。因为能收到重复 ACK 说明网络还能传数据,没必要像超时那样彻底重来。

超时的处理更狠:如果是超时(比 3 个重复 ACK 严重),认为网络严重拥塞,ssthresh 减半、cwnd 直接回到 1,重新慢启动。

完整版教学

一、为什么需要拥塞控制

流量控制只考虑了接收方,但数据从发送方到接收方要经过一堆中间路由器和链路。如果所有发送方都全速发,中间链路会拥塞——路由器缓冲区排队、溢出、大量丢包,触发大量重传,进一步加剧拥塞,最终网络崩溃(拥塞崩溃)

拥塞控制就是让发送方根据网络的拥塞程度自我约束:网络通畅就多发点,出现拥塞迹象(丢包)就主动退让。它是一种「为了整个网络好」的自律,和流量控制(为了接收方)互补。

二、慢启动为什么「慢」,其实增长很快

「慢启动」这名字有点误导——它增长其实是指数级的(每 RTT 翻倍),一点都不慢。它「慢」是指起点低:从 1 个 MSS 开始,而不是一上来就猛发。

为什么要从小开始?因为发送方不知道网络能承受多少。与其一上来就大发特发导致拥塞,不如从小规模试探、快速翻倍逼近网络容量。等到 cwnd 达到阈值 ssthresh(上次拥塞时的经验值),说明快到网络容量了,就切换成谨慎的线性增长(拥塞避免),别再翻倍冒进。

记忆点:慢启动 = 起点低、指数涨;拥塞避免 = 线性涨、谨慎试探。分界线是 ssthresh。

三、加性增、乘性减(AIMD)

拥塞控制的核心哲学是 AIMD(Additive Increase, Multiplicative Decrease,加性增乘性减)

  • 加性增:没拥塞时,cwnd 每个 RTT 缓慢地 +1(线性),小心翼翼地占用更多带宽;
  • 乘性减:一遇到拥塞(丢包),cwnd 大幅减半(乘以 1/2)。

「慢慢加、猛猛减」的不对称设计,是为了快速对拥塞让路、缓慢地争取带宽,让网络能稳定收敛、多个连接公平共享带宽。这是 TCP 拥塞控制稳定的数学基础。

四、快恢复:区分「轻微丢包」和「严重拥塞」

早期 TCP(Tahoe)不管什么丢包,一律 cwnd 归 1、重新慢启动,太保守——一个包丢了就把速度打回原点。

后来(Reno)引入快恢复,做了个重要区分:

  • 收到 3 个重复 ACK(快重传触发):说明后续的包还在正常到达(否则不会有重复 ACK),网络只是轻微丢包,没那么糟。所以 cwnd 减半、进入拥塞避免,而不是归 1。
  • 超时:连 ACK 都收不到了,说明网络严重拥塞。这才 cwnd 归 1、重新慢启动。

用「丢包的严重程度」来决定退让的力度,避免了对轻微丢包的过度反应,吞吐更平稳。

五、现代拥塞控制算法

经典的 Reno 基于「丢包」判断拥塞,但在高带宽、有随机丢包的现代网络里不够好。所以有了改进:

  • CUBIC(Linux 默认):用三次函数调整 cwnd,在高带宽长距离网络下更高效;
  • BBR(Google):不靠丢包,而是主动测量带宽和 RTT来建模网络,避免把缓冲区填满(bufferbloat),在有随机丢包的网络下表现更好。

面试能提一句「现代用 CUBIC/BBR,不再单纯靠丢包判断」是加分项。

六、常见误区

  • ❌ 把拥塞控制和流量控制混为一谈——一个防压垮网络,一个防压垮接收方。
  • ❌ 以为慢启动增长很慢——它是指数增长,只是起点低。
  • ❌ 以为任何丢包都让 cwnd 归 1——只有超时才归 1,3 个重复 ACK 只减半(快恢复)。
  • ❌ 以为拥塞窗口由接收方通告——cwnd 是发送方根据网络状况自己估算的,rwnd 才是接收方通告的。

六、常见误区与追问

考点正确口径
慢启动cwnd 指数增长,快速探测可用带宽
拥塞避免超过 ssthresh 后线性增长
丢包处理超时更重,三次重复 ACK 可快重传快恢复
cwnd starts small
if cwnd < ssthresh: cwnd doubles per RTT
else: cwnd grows linearly
loss -> reduce cwnd

拥塞控制保护的是网络,不是接收端;它调的是拥塞窗口 cwnd。

  • 误区:慢启动真的增长很慢。 慢启动初始窗口小,但每个 RTT 近似翻倍,增长速度其实很快。
  • 误区:拥塞控制和流量控制是一回事。 拥塞控制看网络承载能力,流量控制看接收端缓冲能力。
  • 误区:一丢包就说明接收端处理不过来。 丢包更常被 TCP 当作网络拥塞信号,触发窗口收缩。
  • 追问:cwnd 和 rwnd 谁决定发送量? 发送端实际可发窗口通常取 min(cwnd, rwnd)
  • 追问:快重传为什么比超时轻? 收到重复 ACK 说明网络仍在传包,可能只是局部丢失,不必像超时那样重置到很小。
  • 追问:现代算法如 BBR 有什么不同? BBR 更关注带宽和 RTT 的估计,不完全依赖丢包作为拥塞信号。

七、加强记忆

拥塞控制防止压垮网络,靠拥塞窗口 cwnd 调速:慢启动(从 1 起指数增长探测带宽)→ 到 ssthresh 转拥塞避免(线性增长)→ 丢包时快重传 + 快恢复(3 个重复 ACK 就把 cwnd 减半,而超时才归 1 重新慢启动)。核心哲学是 AIMD「加性增、乘性减」,现代算法有 CUBIC、BBR。它和流量控制的区别是防网络 vs 防接收方