Appearance
TCP 拥塞控制
考情分析
TCP 拥塞控制是 408 计算机网络传输层的绝对核心,几乎每年都会以选择题或大题的形式出现。常见考法包括:给定初始 ssthresh 和某轮次发生超时/收到三个重复 ACK,要求画出 cwnd 变化曲线或计算某一轮次的 cwnd 值。
考频:★★★★★
拥塞的概念
网络中的链路容量、交换节点的缓存和处理能力都是有限的。当网络中的数据量超过了这些资源的承载能力时,网络性能就会急剧下降——丢包增多、时延增大、吞吐量反而降低。这种现象就是拥塞。
拥塞是一个全局性问题,涉及网络中所有主机、所有路由器以及与降低网络传输性能有关的所有因素。
拥塞控制 vs 流量控制
| 对比项 | 拥塞控制 | 流量控制 |
|---|---|---|
| 解决的问题 | 网络中整体负载过大 | 发送方发太快,接收方来不及处理 |
| 作用范围 | 全局性,涉及所有主机和路由器 | 点对点,只涉及发送方和接收方 |
| 控制变量 | 拥塞窗口 cwnd | 接收窗口 rwnd |
| 触发条件 | 丢包(超时或重复ACK) | 接收方通告窗口变化 |
TCP 发送方的实际发送窗口取两者较小值:
四种拥塞控制算法
TCP 拥塞控制包含四种算法:慢开始、拥塞避免、快重传、快恢复。前两种是 TCP Tahoe 引入的,后两种是 TCP Reno 在此基础上增加的改进。
慢开始(Slow Start)
"慢开始"这个名字容易产生误解——它并不是说增长慢,而是说起点低。发送方不会一上来就发送大量数据,而是从一个很小的窗口开始,逐步试探网络容量。
算法过程:
- 连接建立后,初始化
(单位为 MSS,最大报文段长度) - 每收到一个新的 ACK,
- 由于一个 RTT 内所有报文段都会被确认,所以每经过一个 RTT,cwnd 翻倍
也就是说,慢开始阶段 cwnd 按
退出条件: 当
传输轮次: 0 1 2 3 4 5
cwnd: 1 2 4 8 16 32
↑ 若 ssthresh=16,到16后转拥塞避免拥塞避免(Congestion Avoidance)
当 cwnd 达到 ssthresh 后,继续指数增长风险太大,改为线性增长(也叫"加法增大",Additive Increase)。
算法过程:
- 每经过一个 RTT,
- cwnd 按
的规律线性增长
"拥塞避免"并非完全避免拥塞,只是让 cwnd 增长得更谨慎,降低触发拥塞的概率。
遇到超时(网络拥塞的明确信号):
- 重新执行慢开始
这就是所谓的"乘法减小"(Multiplicative Decrease),和"加法增大"合称 AIMD(Additive Increase Multiplicative Decrease)策略。
快重传(Fast Retransmit)
在没有快重传机制时,发送方只能靠超时来判断丢包。但超时定时器的时间往往设得比较长,等待超时会白白浪费大量时间。
快重传的思路:利用冗余 ACK 来更早地发现丢包。
算法过程:
- 接收方收到失序报文段时,立即发送对已收到的最后一个按序报文段的重复确认(不等待延迟确认定时器)
- 发送方收到3 个重复 ACK(即连续 4 个相同的 ACK),立即重传丢失的报文段,不必等待超时
为什么是 3 个重复 ACK?1-2 个重复 ACK 可能只是报文段乱序,并非真的丢包。3 个重复 ACK 是一个比较可靠的丢包判据,同时又不会等待太久。
快恢复(Fast Recovery)
当发送方通过 3 个重复 ACK 判断发生了丢包时,说明网络虽然有拥塞但还没有严重到完全不通(否则连重复 ACK 都收不到)。因此没必要像超时那样把 cwnd 降到 1 重新慢开始。
TCP Reno 的快恢复算法:
(即 cwnd 减半) - 直接进入拥塞避免阶段(线性增长)
对比超时的处理方式:
| 事件 | ssthresh | cwnd | 下一步 |
|---|---|---|---|
| 超时 | cwnd/2 | 1 | 慢开始 |
| 3个重复ACK | cwnd/2 | ssthresh(即cwnd/2) | 拥塞避免 |
交互可视化
下面的可视化工具可以动态展示 cwnd 随传输轮次的变化曲线。可以手动设置 ssthresh、模拟超时和三个重复 ACK 事件,观察四种算法的切换过程。
cwnd 变化过程实例
下面通过一个完整的例子,展示 cwnd 的逐轮变化。假设初始
| 轮次 | cwnd | 阶段 | 说明 |
|---|---|---|---|
| 0 | 1 | 慢开始 | 初始状态 |
| 1 | 2 | 慢开始 | 指数增长 |
| 2 | 4 | 慢开始 | 指数增长 |
| 3 | 8 | 慢开始 | 指数增长 |
| 4 | 16 | 慢开始 | cwnd = ssthresh,下一轮转拥塞避免 |
| 5 | 17 | 拥塞避免 | 线性增长 +1 |
| 6 | 18 | 拥塞避免 | 线性增长 +1 |
| 7 | 19 | 拥塞避免 | 线性增长 +1 |
| 8 | 20 | 拥塞避免 | 线性增长 +1 |
| 9 | 21 | 拥塞避免 | 线性增长 +1 |
| 10 | 22 | 拥塞避免 | 线性增长 +1 |
| 11 | 23 | 拥塞避免 | 线性增长 +1 |
| 12 | 24 | 拥塞避免 | 此轮发生超时 |
| — | — | 调整 | ssthresh = 24/2 = 12,cwnd = 1 |
| 13 | 1 | 慢开始 | 重新开始 |
| 14 | 2 | 慢开始 | 指数增长 |
| 15 | 4 | 慢开始 | 指数增长 |
| 16 | 8 | 慢开始 | 指数增长 |
| 17 | 12 | 慢开始 | cwnd = ssthresh,下一轮转拥塞避免 |
| 18 | 13 | 拥塞避免 | 线性增长 +1 |
| 19 | 14 | 拥塞避免 | 收到3个重复ACK |
| — | — | 快恢复 | ssthresh = 14/2 = 7,cwnd = 7 |
| 20 | 7 | 拥塞避免 | 从快恢复直接进入拥塞避免 |
| 21 | 8 | 拥塞避免 | 线性增长 +1 |
状态转换总览
TCP Tahoe vs TCP Reno
| 对比项 | TCP Tahoe | TCP Reno |
|---|---|---|
| 提出时间 | 1988 | 1990 |
| 包含的算法 | 慢开始 + 拥塞避免 | 慢开始 + 拥塞避免 + 快重传 + 快恢复 |
| 超时处理 | ssthresh=cwnd/2, cwnd=1, 慢开始 | 同 Tahoe |
| 3个重复ACK | 同超时处理(cwnd=1, 慢开始) | ssthresh=cwnd/2, cwnd=ssthresh, 拥塞避免 |
| 性能 | 每次丢包都回到 cwnd=1,恢复慢 | 区分超时和重复ACK,轻度拥塞恢复快 |
| 408考试 | 少数题目会考 Tahoe 行为 | 408 默认考的是 Reno |
关键区别在于对"3 个重复 ACK"的响应。Tahoe 把它当作超时一样处理,直接回到慢开始;Reno 则认为网络还没堵死,只需减半 cwnd 就行。
易错点
1. 慢开始的"慢"是指起点低,不是增长慢
慢开始阶段 cwnd 是指数增长的,增速很快。之所以叫"慢开始",是相对于一开始就把窗口开到最大而言的——它从 1 个 MSS 开始,所以"起步慢"。
2. cwnd = ssthresh 时到底执行哪个算法
408 考试中,
3. 快恢复后 cwnd 的值
收到 3 个重复 ACK 后(Reno):先算
4. 发送窗口不等于 cwnd
实际发送窗口 = min(cwnd, rwnd)。题目如果只提到拥塞控制,默认接收窗口足够大,发送窗口 = cwnd。但如果题目同时给了 rwnd,要取较小值。
高频考点清单
- cwnd 的指数增长阶段(慢开始)和线性增长阶段(拥塞避免)的切换条件
- 超时事件的处理:ssthresh 减半,cwnd 置 1,回到慢开始
- 3 个重复 ACK 的处理(Reno):ssthresh 减半,cwnd = ssthresh,进入拥塞避免
- 给定初始 ssthresh 和事件序列,计算每一轮的 cwnd 值
- 发送窗口 = min(cwnd, rwnd)
- Tahoe 和 Reno 的区别(较少考,但偶尔出现)
- AIMD 策略的含义:加法增大(拥塞避免阶段 +1)、乘法减小(遇到拥塞 /2)