Chapter 3-5:拥塞控制原理、TCP 拥塞控制与传输层演化学习指南

来源:根据你上传的 Chapter3-5.md 对话转录整理。

Chapter 3-5:拥塞控制原理、TCP 拥塞控制与传输层演化学习指南

这一节的核心问题是:

当网络中有很多发送方同时发送数据,导致路由器队列、链路带宽等资源不够用时,TCP 应该如何调节发送速率,既避免把网络挤爆,又尽量充分利用带宽?

前面学习 TCP 可靠传输时,重点是:

  • 数据丢了怎么办;
  • ACK 怎么确认;
  • 超时怎么重传;
  • 如何保证可靠、按序交付。

但这一节进一步追问:

如果很多 TCP 连接都在发送、都在重传、都在增加速率,网络整体会不会越来越糟?

答案是:会。

所以 TCP 不能只会“丢了就重传”,还必须会“网络拥塞时主动降速”。


0. 本节核心主线


❗ 本节核心结论:
拥塞控制解决的不是单个接收方能不能接住数据,而是整个网络能不能承受发送方注入的数据量。

1. 拥塞是什么?

拥塞 congestion 可以直观理解为:

太多数据同时进入网络,超过了网络链路、路由器队列和转发能力。

典型表现有两个:

表现 原因
分组延迟增大 路由器队列中等待转发的分组越来越多
分组丢失 路由器缓存满了,新来的分组只能被丢弃

拥塞不是“某个 TCP 连接自己的问题”,而是多个连接共享网络资源时产生的整体问题。


2. 拥塞控制 vs 流量控制

这两个概念必须分清。

对比项 拥塞控制 congestion control 流量控制 flow control
保护对象 网络 接收方
问题来源 太多发送方把网络挤爆 发送方太快,接收方 buffer 吃不下
典型位置 路由器队列、瓶颈链路 接收端缓存
TCP 中的窗口 cwnd rwnd
本质 不压垮网络 不淹没接收方

一句话区分:

1
2
流量控制怕“接收方吃不下”;
拥塞控制怕“网络扛不住”。

❗ 本节核心结论:
rwnd 管接收方,cwnd 管网络。TCP 实际可发送的未确认数据量要同时受二者限制。

3. 拥塞的代价

拥塞不是简单地“慢一点”,它会真正浪费网络资源。

3.1 无限缓存:没有丢包,但延迟爆炸

假设路由器缓存无限大,分组不会被丢弃。

这并不意味着没有拥塞问题。

当输入速率接近链路容量时,路由器队列会越来越长,分组排队时间会急剧增加。也就是说:

1
不丢包 ≠ 没有拥塞

无限缓存只是避免了丢包,但会制造巨大的排队延迟。

3.2 有限缓存:丢包与重传

现实中路由器缓存是有限的。缓存满了,分组就会被丢弃。

这时 TCP 发送方可能重传,于是网络中出现两类数据:

类型 含义
原始数据 第一次发送的新数据
重传数据 因为丢包、超时或误判而再次发送的数据

课件中区分了:

1
2
λin  = 原始数据输入速率
λ'in = 原始数据 + 重传数据的传输层输入速率

因为有重传,所以:

1
λ'in ≥ λin

如果出现大量重传,链路看起来很忙,但真正交付给应用层的新数据并不多。这会导致 goodput 有效吞吐量 下降。

3.3 多跳路径:上游资源浪费

在多跳网络中,一个分组可能经过了前面多个路由器,消耗了多段链路的带宽,最后却在下游路由器因为拥塞被丢弃。

这意味着:

分组被丢弃时,不只是最后一跳浪费了,前面所有已经为它付出的传输资源也浪费了。


❗ 本节核心结论:
拥塞的代价包括排队延迟、丢包、重传开销、goodput 下降,以及多跳路径中的上游资源浪费。

4. 拥塞控制的两种思路

4.1 端到端拥塞控制

端到端拥塞控制中,网络核心不直接告诉发送方“我拥塞了”。

发送方只能根据现象推断:

  • 超时;
  • 重复 ACK;
  • RTT 变大;
  • 丢包。

TCP Tahoe、TCP Reno 就属于典型端到端拥塞控制。

这种思路符合 TCP/IP 的传统设计原则:

网络核心尽量简单,复杂控制逻辑放在端系统。

4.2 网络辅助拥塞控制

网络辅助拥塞控制中,路由器会显式告诉端系统网络状态。

例如:

  • 标记某个 bit 表示拥塞;
  • 明确告诉发送方应该降速;
  • 通过 ECN 显式拥塞通知提前提醒。

可以类比为:

方法 类比
端到端拥塞控制 司机根据车速和刹车情况自己判断前方拥堵
网络辅助拥塞控制 路边电子屏直接提示“前方拥堵,请降速”

5. TCP 如何感知拥塞?

TCP 发送方无法直接看到路由器队列,所以它只能通过 ACK 的表现来猜测网络状态。

主要有两类信号。

5.1 Timeout:严重拥塞信号

timeout 表示:

1
2
某个 segment 发出后,
在超时时间内没有收到有效 ACK。

这可能意味着:

  • 数据段在路由器队列溢出时被丢弃;
  • ACK 回来的路上丢了;
  • 网络排队延迟过大,ACK 来得太晚。

无论具体原因是什么,TCP 通常把 timeout 当作比较严重的拥塞信号。

处理方式非常保守:

1
2
3
ssthresh = cwnd / 2
cwnd = 1 MSS
重新进入慢启动

课件中也总结了 timeout 时的规则:将 ssthresh 设为当前 cwnd 的一半,并把 cwnd 降到 1 MSS,重新进入慢启动。

5.2 三个重复 ACK:轻微拥塞信号

三个重复 ACK 的典型场景是:

发送方连续发出:

1
2
3
4
5
Seq=100
Seq=120
Seq=140
Seq=160
Seq=180

如果 Seq=100 丢了,但后面的 Seq=120、140、160、180 都到达了接收方。

接收方因为使用累计 ACK,会一直说:

1
2
3
4
ACK=100
ACK=100
ACK=100
ACK=100

含义是:

1
2
我还是在等 Seq=100;
后面的数据虽然到了,但中间有缺口。

所以发送方连续收到 3 个重复 ACK 的典型条件是:

  1. 较早的某个 segment 丢了;
  2. 后续多个 segment 到达了接收方;
  3. 接收方反复确认同一个“下一个期待字节”。

课件中也强调:三个重复 ACK 表示后续报文段已经乱序到达,因此缺失报文段很可能丢失。

为什么说这是“轻微拥塞”?

因为后面的数据段还能到达接收方,ACK 也还能返回发送方。这说明网络虽然出现了丢包,但并没有完全堵死。


❗ 本节核心结论:
timeout 表示发送方等不到有效反馈,网络可能严重拥塞;3 个重复 ACK 表示中间有丢失,但后续数据还能流动,所以是相对轻微的拥塞信号。

6. cwnd:TCP 控制发送速率的核心变量

TCP 发送方维护一个 congestion window,拥塞窗口,记作 cwnd

它表示:

在没有收到确认之前,发送方最多可以向网络中注入多少未确认数据。

TCP 对未确认数据量的限制是:

1
LastByteSent - LastByteAcked ≤ min(cwnd, rwnd)

其中:

符号 含义
cwnd 拥塞窗口,保护网络
rwnd 接收窗口,保护接收方
LastByteSent - LastByteAcked 已发送但尚未确认的数据量,也叫 in-flight data

TCP 发送速率可以粗略写成:

1
TCP rate ≈ cwnd / RTT

含义是:

在一个 RTT 内,发送方大约可以发送 cwnd 大小的数据。

所以:

  • cwnd 越大,发送速率越高;
  • RTT 越大,同样的 cwnd 对应的发送速率越低。

7. 慢启动 Slow Start

慢启动是 TCP 刚开始发送时的增长策略。

7.1 为什么需要慢启动?

TCP 刚建立连接时,不知道网络能承受多大发送速率。

如果一开始就猛发,可能瞬间造成拥塞。
如果一直很慢,又浪费带宽。

所以 TCP 采用:

1
从很小开始,但快速增长。

7.2 慢启动为什么不是真的“慢”?

慢启动慢在起点低,不慢在增长速度。

初始时通常:

1
cwnd = 1 MSS

慢启动阶段规则是:

1
每收到一个 ACK,cwnd 增加 1 MSS

一个 RTT 内大约会收到当前窗口数量对应的 ACK,所以整体效果是:

1
每经过一个 RTT,cwnd 大约翻倍

例如:

1
1 MSS → 2 MSS → 4 MSS → 8 MSS → 16 MSS

❗ 本节核心结论:
慢启动不是“增长慢”,而是“从低速开始的指数增长”。

8. ssthresh:从慢启动到拥塞避免的分界线

ssthresh 全称是 slow start threshold,即慢启动阈值。

它决定 TCP 什么时候从指数增长切换到线性增长。

基本规则是:

1
2
cwnd < ssthresh   → 慢启动,指数增长
cwnd ≥ ssthresh → 拥塞避免,线性增长

课件中明确总结:ssthresh 决定 TCP 什么时候从指数增长进入线性增长;timeout 和 3 个重复 ACK 都会把 ssthresh 设为当前 cwnd / 2

8.1 ssthresh = cwnd / 2 怎么理解?

当发生拥塞时,TCP 认为:

当前 cwnd 已经接近或超过网络承载能力。

所以它不敢再把这个窗口当成安全值,而是将它打个对折,作为下次谨慎试探的边界。

例如发生拥塞时:

1
cwnd = 32 MSS

那么:

1
ssthresh = 16 MSS

意思是:

1
2
3
上次窗口开到 32 MSS 时撞墙了;
下次先指数增长到 16 MSS 附近;
然后改成线性增长,小心试探。

这就是 ssthresh = cwnd / 2 的直觉:
把上次撞墙的位置打对折,作为下一次从激进增长切换到谨慎增长的分界线。


9. 拥塞避免 AIMD

AIMD 全称是:

1
Additive Increase, Multiplicative Decrease

即:

1
加性增,乘性减

9.1 Additive Increase:加性增

当没有检测到拥塞时,TCP 在拥塞避免阶段会让 cwnd 线性增长:

1
每个 RTT 大约增加 1 MSS

例如:

1
16 → 17 → 18 → 19 → 20

这比慢启动的指数增长保守得多。

9.2 Multiplicative Decrease:乘性减

当检测到拥塞时,TCP 会显著降低 cwnd

常见规则是:

1
ssthresh = cwnd / 2

也就是把拥塞窗口减半。

这会形成 TCP 拥塞控制的典型“锯齿形”变化:


❗ 本节核心结论:
AIMD 的思想是:没拥塞时慢慢加速,发现拥塞时明显降速,然后重新试探。

10. timeout 与 3 个重复 ACK 后的状态变化

这是本节最重要的状态机逻辑。

10.1 timeout 后

假设发生 timeout 时:

1
cwnd = 32 MSS

TCP 会设置:

1
2
ssthresh = 16 MSS
cwnd = 1 MSS

然后重新慢启动:

1
1 → 2 → 4 → 8 → 16

到达 ssthresh = 16 后,进入拥塞避免:

1
16 → 17 → 18 → 19 ...

timeout 说明网络情况可能比较严重,所以 TCP 直接退回最低速率。

10.2 3 个重复 ACK 后

如果收到 3 个重复 ACK,TCP Reno 认为:

有一个段可能丢了,但后续段还能到达,网络仍然有传输能力。

于是它不会把 cwnd 降到 1 MSS,而是:

1
2
3
4
ssthresh = cwnd / 2
cwnd = ssthresh + 3 MSS
重传丢失段
进入快速恢复 / 拥塞避免

这里 + 3 MSS 的含义是:

3 个重复 ACK 表示有 3 个后续 segment 已经离开网络并到达接收方,所以网络中相当于腾出了 3 个 segment 的空间。

课件中也说明:收到三个重复 ACK 时,Reno 将 ssthresh 设为 cwnd/2,并将 cwnd 设为 ssthresh + 3 MSS;而 timeout 则把 cwnd 降到 1 MSS


11. 快速重传与快速恢复

11.1 快速重传 Fast Retransmit

快速重传指的是:

收到 3 个重复 ACK 后,不等 timeout,立即重传推测丢失的 segment。

它的目的很明确:

1
减少等待 timeout 的时间,更快修复丢失。

11.2 快速恢复 Fast Recovery

快速恢复是 TCP Reno 的策略。

收到 3 个重复 ACK 后,Reno 不会像 timeout 那样回到 1 MSS,而是把窗口大致减半,并进入快速恢复 / 拥塞避免。

原因是:

3 个重复 ACK 说明网络仍然在传输后续数据,不需要像严重拥塞那样完全退回起点。


12. TCP Tahoe vs TCP Reno

对比项 TCP Tahoe TCP Reno
timeout cwnd = 1 MSS cwnd = 1 MSS
3 个重复 ACK cwnd = 1 MSS cwnd 大致减半
快速重传
快速恢复
保守程度 更保守 更激进
核心差异 不区分 timeout 和 3 dup ACK 的处理强度 区分严重拥塞和轻微拥塞

为什么说 Reno 更激进?

不是说 Reno 更鲁莽,而是说它对 3 个重复 ACK 的判断更乐观:

  • Tahoe:只要检测到丢失,就把 cwnd 降到 1 MSS
  • Reno:如果是 3 个重复 ACK,只把 cwnd 减半,不回到最低速。

例如丢失发生时:

1
cwnd = 32 MSS

Tahoe:

1
2
3
cwnd = 1 MSS
重新慢启动
1 → 2 → 4 → 8 → 16 ...

Reno:

1
2
3
ssthresh = 16 MSS
cwnd ≈ 16 MSS 附近
进入快速恢复 / 拥塞避免

所以 Reno 更激进,是因为它保留了一半左右的发送能力,而不是把发送速率打回最低点。课件中也明确对比:Tahoe 在 3 个重复 ACK 或 timeout 时都会降到 1 MSS,而 Reno 在 3 个重复 ACK 时只减半并快速恢复。


❗ 本节核心结论:
Tahoe 更保守:一丢就回到 1 MSS;Reno 更激进:只有 timeout 才回到 1 MSS,3 个重复 ACK 只减半。

13. TCP CUBIC、ECN 与传输层演化

13.1 TCP CUBIC

传统 TCP Reno 使用 AIMD,窗口线性增长。

TCP CUBIC 的直觉是:

上次丢包时的窗口 Wmax 很可能接近网络瓶颈,所以降速后可以较快接近 Wmax,接近后再谨慎增长。

CUBIC 用立方函数调整窗口增长速度。它相比 Reno 更适合高速、长延迟网络。

13.2 ECN:显式拥塞通知

ECN 是 Explicit Congestion Notification

它的思想是:

路由器不必等队列满了再丢包,而是可以提前给分组做拥塞标记,让端系统知道网络正在拥塞。

这样 TCP 不必完全依赖丢包来判断拥塞。

13.3 为什么 TCP/UDP 之后仍然需要传输层演化?

因为 TCP 和 UDP 各有局限:

协议 局限
TCP 可靠性、拥塞控制、连接管理带来开销;把丢包视为拥塞在某些网络中不总是准确
UDP 太轻量,只提供基本复用/解复用,可靠性和拥塞控制要应用自己做

现代网络环境更复杂,包括:

  • 高速链路;
  • 数据中心网络;
  • 无线网络;
  • 移动网络;
  • 低延迟应用;
  • 视频、游戏、实时通信。

所以传输层还在继续演化。


14. 易混点整理

14.1 拥塞控制不是流量控制

  • rwnd:接收方还能接多少;
  • cwnd:网络还能承受多少。

14.2 慢启动不是增长慢

慢启动是从小窗口开始,但增长是指数级的。

14.3 timeout 和 3 个重复 ACK 不是一回事

信号 网络状态判断 TCP 行为
timeout 严重拥塞 cwnd = 1 MSS
3 个重复 ACK 轻微拥塞 cwnd 大致减半

14.4 cwnd 不是越大越好

cwnd 增大可以提高发送速率,但如果超过网络瓶颈,会导致:

  • 排队延迟;
  • 丢包;
  • 重传;
  • goodput 下降。

14.5 重传不是免费的

重传会占用链路容量。
如果是非必要重传,还会进一步加剧拥塞。

14.6 TCP 是试探式控制

TCP 并不知道网络真实剩余带宽,只能根据 ACK、重复 ACK、timeout 等现象推断。

可以理解为:

1
2
3
正常 ACK → 还能加速
3 个重复 ACK → 有丢包,但网络还在流动,减半
timeout → 反馈都等不到,退回最低速

15. 本节一句话总复习

TCP 拥塞控制的核心是:发送方通过 ACK 的表现推断网络状态,用 cwnd 控制未确认数据量;正常 ACK 时增加 cwnd,3 个重复 ACK 时认为轻微拥塞并减半,timeout 时认为严重拥塞并退回 1 MSS。慢启动用于快速接近可用带宽,拥塞避免用于谨慎试探瓶颈,快速恢复用于在网络仍有传输能力时避免过度降速。


16. 自测题:暂不附答案

  1. 为什么说拥塞控制解决的是“网络整体问题”,而流量控制解决的是“接收端个体问题”?

  2. 如果路由器缓存无限大,是否就不会有拥塞问题?请从延迟角度解释。

  3. 为什么有限缓存场景下,传输层输入速率 λ'in 可能大于原始数据输入速率 λin

  4. 什么是 goodput?为什么发生非必要重传时,链路吞吐量可能很高,但 goodput 反而下降?

  5. 多跳路径中,如果分组在最后一跳前被丢弃,为什么说前面链路的传输能力也被浪费了?

  6. 端到端拥塞控制为什么符合 TCP/IP “网络核心简单”的设计思想?

  7. 为什么 timeout 被看作严重拥塞,而 3 个重复 ACK 被看作轻微拥塞?

  8. 发送方在什么情况下会连续收到 3 个重复 ACK?

  9. cwndrwnd 分别保护谁?TCP 实际可发送的未确认数据量为什么要取二者较小值?

  10. 为什么 TCP 发送速率可以粗略写成 cwnd / RTT

  11. 慢启动阶段“每收到一个 ACK,cwnd 加 1 MSS”和“每个 RTT,cwnd 翻倍”为什么近似等价?

  12. 慢启动为什么起点低,但增长却很快?

  13. ssthresh 的作用是什么?为什么常设为拥塞发生时 cwnd 的一半?

  14. AIMD 中“加性增”和“乘性减”分别对应 TCP 在什么情况下的行为?

  15. timeout 发生后,cwndssthresh 分别如何变化?

  16. 3 个重复 ACK 发生后,TCP Reno 的 cwndssthresh 如何变化?

  17. 为什么 TCP Reno 收到 3 个重复 ACK 时不把 cwnd 直接降到 1 MSS

  18. Tahoe 和 Reno 的核心区别是什么?为什么说 Reno 更激进?

  19. TCP CUBIC 相比 Reno 的基本思想有什么不同?

  20. 为什么 TCP/UDP 之后仍然需要传输层协议继续演化?


最终总结

Chapter 3-5 的核心是 TCP 拥塞控制

它和前面可靠传输的区别在于:

  • 可靠传输关心“数据丢了怎么补”;
  • 拥塞控制关心“为什么会丢,以及发送方是否应该降速”。

TCP 通过 cwnd 控制网络中未确认数据量,通过 ACK 的不同表现推断拥塞程度:

  • 正常 ACK:网络还能承受,逐步增加 cwnd
  • 3 个重复 ACK:有丢包,但网络仍在流动,cwnd 大致减半;
  • timeout:反馈迟迟不来,可能严重拥塞,cwnd 降到 1 MSS

❗ 最终核心结论:
TCP 拥塞控制不是精确知道网络带宽,而是通过试探和反馈调节发送速率:慢启动快速接近带宽,拥塞避免谨慎探测瓶颈,快速重传/快速恢复在轻微拥塞时减少恢复成本,timeout 则触发最保守的降速。