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

Chapter 3-5:拥塞控制原理、TCP 拥塞控制与传输层演化学习指南
agsd来源:根据你上传的 Chapter3-5.md 对话转录整理。
Chapter 3-5:拥塞控制原理、TCP 拥塞控制与传输层演化学习指南
这一节的核心问题是:
当网络中有很多发送方同时发送数据,导致路由器队列、链路带宽等资源不够用时,TCP 应该如何调节发送速率,既避免把网络挤爆,又尽量充分利用带宽?
前面学习 TCP 可靠传输时,重点是:
- 数据丢了怎么办;
- ACK 怎么确认;
- 超时怎么重传;
- 如何保证可靠、按序交付。
但这一节进一步追问:
如果很多 TCP 连接都在发送、都在重传、都在增加速率,网络整体会不会越来越糟?
答案是:会。
所以 TCP 不能只会“丢了就重传”,还必须会“网络拥塞时主动降速”。
0. 本节核心主线
flowchart TD
A[多个发送方共享网络] --> B[链路和路由器缓存成为瓶颈]
B --> C[排队延迟增大]
C --> D[缓存溢出导致丢包]
D --> E[发送方重传]
E --> F[网络负载进一步增加]
F --> G[需要拥塞控制]
G --> H[TCP 用 cwnd 调节发送速率]
H --> I[慢启动 / 拥塞避免 / 快速恢复]
❗ 本节核心结论:
拥塞控制解决的不是单个接收方能不能接住数据,而是整个网络能不能承受发送方注入的数据量。
1. 拥塞是什么?
拥塞 congestion 可以直观理解为:
太多数据同时进入网络,超过了网络链路、路由器队列和转发能力。
典型表现有两个:
| 表现 | 原因 |
|---|---|
| 分组延迟增大 | 路由器队列中等待转发的分组越来越多 |
| 分组丢失 | 路由器缓存满了,新来的分组只能被丢弃 |
拥塞不是“某个 TCP 连接自己的问题”,而是多个连接共享网络资源时产生的整体问题。
2. 拥塞控制 vs 流量控制
这两个概念必须分清。
| 对比项 | 拥塞控制 congestion control | 流量控制 flow control |
|---|---|---|
| 保护对象 | 网络 | 接收方 |
| 问题来源 | 太多发送方把网络挤爆 | 发送方太快,接收方 buffer 吃不下 |
| 典型位置 | 路由器队列、瓶颈链路 | 接收端缓存 |
| TCP 中的窗口 | cwnd |
rwnd |
| 本质 | 不压垮网络 | 不淹没接收方 |
一句话区分:
1 | 流量控制怕“接收方吃不下”; |
❗ 本节核心结论:
rwnd 管接收方,cwnd 管网络。TCP 实际可发送的未确认数据量要同时受二者限制。
3. 拥塞的代价
拥塞不是简单地“慢一点”,它会真正浪费网络资源。
3.1 无限缓存:没有丢包,但延迟爆炸
假设路由器缓存无限大,分组不会被丢弃。
这并不意味着没有拥塞问题。
当输入速率接近链路容量时,路由器队列会越来越长,分组排队时间会急剧增加。也就是说:
1 | 不丢包 ≠ 没有拥塞 |
无限缓存只是避免了丢包,但会制造巨大的排队延迟。
3.2 有限缓存:丢包与重传
现实中路由器缓存是有限的。缓存满了,分组就会被丢弃。
这时 TCP 发送方可能重传,于是网络中出现两类数据:
| 类型 | 含义 |
|---|---|
| 原始数据 | 第一次发送的新数据 |
| 重传数据 | 因为丢包、超时或误判而再次发送的数据 |
课件中区分了:
1 | λ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 | 某个 segment 发出后, |
这可能意味着:
- 数据段在路由器队列溢出时被丢弃;
- ACK 回来的路上丢了;
- 网络排队延迟过大,ACK 来得太晚。
无论具体原因是什么,TCP 通常把 timeout 当作比较严重的拥塞信号。
处理方式非常保守:
1 | ssthresh = cwnd / 2 |
课件中也总结了 timeout 时的规则:将 ssthresh 设为当前 cwnd 的一半,并把 cwnd 降到 1 MSS,重新进入慢启动。
5.2 三个重复 ACK:轻微拥塞信号
三个重复 ACK 的典型场景是:
发送方连续发出:
1 | Seq=100 |
如果 Seq=100 丢了,但后面的 Seq=120、140、160、180 都到达了接收方。
接收方因为使用累计 ACK,会一直说:
1 | ACK=100 |
含义是:
1 | 我还是在等 Seq=100; |
所以发送方连续收到 3 个重复 ACK 的典型条件是:
- 较早的某个 segment 丢了;
- 后续多个 segment 到达了接收方;
- 接收方反复确认同一个“下一个期待字节”。
课件中也强调:三个重复 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 | 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 | 上次窗口开到 32 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 拥塞控制的典型“锯齿形”变化:
flowchart LR
A[逐渐增大 cwnd] --> B[检测到拥塞]
B --> C[cwnd 降低]
C --> D[再次逐渐增大]
D --> B
❗ 本节核心结论:
AIMD 的思想是:没拥塞时慢慢加速,发现拥塞时明显降速,然后重新试探。
10. timeout 与 3 个重复 ACK 后的状态变化
这是本节最重要的状态机逻辑。
10.1 timeout 后
假设发生 timeout 时:
1 | cwnd = 32 MSS |
TCP 会设置:
1 | ssthresh = 16 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 | ssthresh = cwnd / 2 |
这里 + 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 | cwnd = 1 MSS |
Reno:
1 | ssthresh = 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 | 正常 ACK → 还能加速 |
15. 本节一句话总复习
TCP 拥塞控制的核心是:发送方通过 ACK 的表现推断网络状态,用 cwnd 控制未确认数据量;正常 ACK 时增加 cwnd,3 个重复 ACK 时认为轻微拥塞并减半,timeout 时认为严重拥塞并退回 1 MSS。慢启动用于快速接近可用带宽,拥塞避免用于谨慎试探瓶颈,快速恢复用于在网络仍有传输能力时避免过度降速。
16. 自测题:暂不附答案
为什么说拥塞控制解决的是“网络整体问题”,而流量控制解决的是“接收端个体问题”?
如果路由器缓存无限大,是否就不会有拥塞问题?请从延迟角度解释。
为什么有限缓存场景下,传输层输入速率
λ'in可能大于原始数据输入速率λin?什么是 goodput?为什么发生非必要重传时,链路吞吐量可能很高,但 goodput 反而下降?
多跳路径中,如果分组在最后一跳前被丢弃,为什么说前面链路的传输能力也被浪费了?
端到端拥塞控制为什么符合 TCP/IP “网络核心简单”的设计思想?
为什么 timeout 被看作严重拥塞,而 3 个重复 ACK 被看作轻微拥塞?
发送方在什么情况下会连续收到 3 个重复 ACK?
cwnd和rwnd分别保护谁?TCP 实际可发送的未确认数据量为什么要取二者较小值?为什么 TCP 发送速率可以粗略写成
cwnd / RTT?慢启动阶段“每收到一个 ACK,
cwnd加 1 MSS”和“每个 RTT,cwnd翻倍”为什么近似等价?慢启动为什么起点低,但增长却很快?
ssthresh的作用是什么?为什么常设为拥塞发生时cwnd的一半?AIMD 中“加性增”和“乘性减”分别对应 TCP 在什么情况下的行为?
timeout 发生后,
cwnd和ssthresh分别如何变化?3 个重复 ACK 发生后,TCP Reno 的
cwnd和ssthresh如何变化?为什么 TCP Reno 收到 3 个重复 ACK 时不把
cwnd直接降到1 MSS?Tahoe 和 Reno 的核心区别是什么?为什么说 Reno 更激进?
TCP CUBIC 相比 Reno 的基本思想有什么不同?
为什么 TCP/UDP 之后仍然需要传输层协议继续演化?
最终总结
Chapter 3-5 的核心是 TCP 拥塞控制。
它和前面可靠传输的区别在于:
- 可靠传输关心“数据丢了怎么补”;
- 拥塞控制关心“为什么会丢,以及发送方是否应该降速”。
TCP 通过 cwnd 控制网络中未确认数据量,通过 ACK 的不同表现推断拥塞程度:
- 正常 ACK:网络还能承受,逐步增加
cwnd; - 3 个重复 ACK:有丢包,但网络仍在流动,
cwnd大致减半; - timeout:反馈迟迟不来,可能严重拥塞,
cwnd降到1 MSS。
❗ 最终核心结论:
TCP 拥塞控制不是精确知道网络带宽,而是通过试探和反馈调节发送速率:慢启动快速接近带宽,拥塞避免谨慎探测瓶颈,快速重传/快速恢复在轻微拥塞时减少恢复成本,timeout 则触发最保守的降速。








