TCP 拥塞控制
拥塞控制是指防止过多的数据注入网络,保证网络中的路由器或链路不致过载。出现拥塞时,端点并不了解拥塞发生的细节,对通信的端点来说,拥塞往往表现为通信时延的增加。
这一节是第 5 章出计算题最多的地方:发送窗口、接收窗口和拥塞窗口的关系(2010、2014、2015、2016)、慢开始的原理(2014、2015)、慢开始门限的作用(2017、2020、2023)、快重传的时机(2019)都考过。本页两道错题都是”超时之后第几个 RTT 窗口是多少”。
机制
拥塞控制与流量控制的区别
拥塞控制是让网络能够承受现有的网络负荷,是一个全局性的过程,涉及所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是指点对点的通信量的控制,是个端到端的问题(接收端控制发送端),它所要做的是抑制发送端发送数据的速率,以便使接收端来得及接收。
两者也有相似的地方,即它们都通过控制发送方发送数据的速率来达到控制效果。王道的例子:某个链路的传输速率为 10Gb/s,某大型机向一台 PC 以 1Gb/s 的速率传送文件,显然网络的带宽是足够大的,因而不存在拥塞问题,但如此高的发送速率将导致 PC 可能来不及接收,因此必须进行流量控制;但若有 100 万台 PC 在此链路上以 1Mb/s 的速率传送文件,则现在的问题就变为网络的负载是否超过了现有网络所能承受的范围,这是拥塞控制要解决的(4.1.5)。
拥塞窗口与发送窗口
发送方在确定发送报文段的速率时,既要考虑接收方的接收能力,又要从全局考虑不要使网络发生拥塞。因此,除了接收窗口,TCP 还要求发送方维持一个拥塞窗口(cwnd),其大小取决于网络的拥塞程度,并且动态地变化。发送方控制拥塞窗口的原则是:只要网络未出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去,以提高网络的利用率;但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入网络的分组数,以缓解网络出现的拥塞。
下面假设:数据为单方向传送,对方只传送确认报文;接收方总是有足够大的缓存空间,因而发送窗口的大小由网络的拥塞程度决定。为了便于理解,采用最大报文段长度 MSS 作为拥塞窗口大小的单位。
慢开始与拥塞避免
慢开始算法的思路是:当发送方刚开始发送数据时,因为并不清楚网络的负荷情况,若立即把大量数据注入网络,则有可能引发网络拥塞。具体方法是:先发送少量数据探测一下,若没有发生拥塞,则适当增大拥塞窗口(慢开始算法的原理 2014、2015 年考过)。
例如,A 向 B 发送数据,发送方先令 cwnd=1,即一个 MSS。A 发送第一个报文段,A 收到 B 对第一个报文段的确认后,把 cwnd 从 1 增大到 2。于是 A 接着发送两个报文段,A 收到 B 对这两个报文段的确认后,把 cwnd 从 2 增大到 4,下次就可一次发送 4 个报文段。
慢开始的”慢”并不是指拥塞窗口 cwnd 的增长速率慢,而是指在 TCP 开始发送报文段时先设置 cwnd=1,使得发送方一开始向网络注入的报文段少(目的是试探一下网络的拥塞情况),然后逐渐增大 cwnd。使用慢开始算法后,每经过一个传输轮次(往返时延 RTT),cwnd 就会加倍,即 cwnd 的值随传输轮次指数增长。为了防止 cwnd 增长过大而引起网络拥塞,还需要设置一个慢开始门限 ssthresh(阈值)。
拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢增大,具体做法是:每经过一个往返时延 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍,使拥塞窗口 cwnd 按线性规律缓慢增长(加法增大),这比慢开始算法的拥塞窗口增长速率要缓慢得多(慢开始和拥塞避免算法的原理、慢开始门限的作用 2017、2020、2023 年考过)。
根据 cwnd 的大小执行不同的算法:
| 条件 | 使用的算法 |
|---|---|
| cwnd < ssthresh | 慢开始算法 |
| cwnd > ssthresh | 停止使用慢开始算法而改用拥塞避免算法 |
| cwnd = ssthresh | 既可使用慢开始算法,又可使用拥塞避免算法(常规做法) |
网络拥塞的处理:无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(未按时收到确认),就要首先把慢开始门限 ssthresh 设置为出现拥塞时的发送方的 cwnd 值的一半(但不能小于 2),然后把拥塞窗口 cwnd 重新设置为 1,执行慢开始算法。这样做的目的是迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完。
王道的例子(图 5.10,慢开始和拥塞避免阶段的平均传输速率分析 2016、2023 年考过):初始时 cwnd=1,ssthresh=16。
| 传输轮次 | 0 | 1 | 2 | 3 | 4 | 5~12 | 12 之后 | 13 | 14 | 15 | 16 | 17 | 18~22 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| cwnd | 1 | 2 | 4 | 8 | 16 | 17,18,…,24 | 超时 | 1 | 2 | 4 | 8 | 12 | 13,14,… |
- 慢开始阶段,发送方每收到一个对新报文段的确认 ACK,就把 cwnd 值加 1,也即经过每个传输轮次(RTT),cwnd 呈指数规律增长。当 cwnd 增长到慢开始门限 ssthresh 时(cwnd=16),就改用拥塞避免算法,cwnd 按线性规律增长。
- 当 cwnd=24 时,网络出现超时,调整 ssthresh 值为 12(超时时 cwnd 值的一半),同时 cwnd 置为 1,并执行慢开始算法;当 cwnd=12 时,改为执行拥塞避免算法。
王道的注意框:在慢开始阶段,若
在慢开始和拥塞避免算法中使用了”乘法减小”和”加法增大”方法。“乘法减小”是指不论是在慢开始阶段还是在拥塞避免阶段,只要出现超时(很可能出现了网络拥塞),就把慢开始门限值 ssthresh 设置为当前拥塞窗口的一半(并执行慢开始算法)。当网络频繁出现拥塞时,ssthresh 值就下降得很快,以大大减少注入网络的分组数。而”加法增大”是指执行拥塞避免算法后,在收到对所有报文段的确认后(经过 1RTT),就把拥塞窗口 cwnd 增加一个 MSS 大小,使拥塞窗口缓慢增大,以防止网络过早出现拥塞。拥塞避免并不能完全避免拥塞,它是指在拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞。
快重传与快恢复
有时个别报文段会在网络中丢失,但实际上网络并未发生拥塞。若发送方迟迟收不到确认,就会产生超时,并误认为网络发生了拥塞,这就导致发送方错误地启动慢开始算法,从而降低传输效率。
快重传算法是使发送方尽快(尽早)进行重传,而不等超时计时器超时再重传。这就要求接收方不要等待自己发送数据时才进行捎带确认,而要立即发送确认,即使收到了失序的报文段也要立即发出对已收到报文段的重复确认。发送方一旦连续收到 3 个冗余 ACK(重复确认),就立即重传相应的报文段,而不是等该报文段的超时计时器超时再重传(快重传算法的原理、重传的时机 2019 年考过;冗余 ACK 见 5.3.4)。
快恢复算法的原理如下:当发送方连续收到 3 个冗余 ACK(重复确认)时,执行”乘法减小”方法,把慢开始门限 ssthresh 调整为当前 cwnd 的一半。这是为了预防网络发生拥塞。但发送方现在认为网络很可能没有发生(严重)拥塞,否则就不会有几个报文段连续到达接收方,也不会连续收到重复确认。因此与慢开始算法的不同之处是,它把 cwnd 值也调整为当前 cwnd 的一半(等于 ssthresh 值),然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。因为跳过了拥塞窗口 cwnd 从 1 起始的慢开始过程,所以被称为快恢复。
四种算法的使用总结:
| 触发事件 | 处理 |
|---|---|
| TCP 连接建立、网络出现超时 | 慢开始 + 拥塞避免:ssthresh = cwnd/2,cwnd = 1 |
| 发送方收到 3 个冗余 ACK | 快重传 + 快恢复:ssthresh = cwnd/2,cwnd = ssthresh |
在流量控制中,发送方发送数据的量由接收方决定;而在拥塞控制中,则由发送方自己通过检测网络状况来决定。接收方的缓存空间总是有限的,因此发送方发送窗口的大小由流量控制和拥塞控制共同决定:当题目中同时出现接收窗口(rwnd)和拥塞窗口(cwnd)时,发送方发送窗口的实际大小是由 rwnd 和 cwnd 中较小的那一个确定的。
计算模板
超时后第 个 RTT 的 cwnd
- 记下超时时刻的 cwnd,令 ssthresh = cwnd/2(不小于 2),cwnd = 1。
- 慢开始阶段:每经过 1 个 RTT,cwnd 加倍,但一旦加倍会超过 ssthresh,就取 ssthresh。
- 拥塞避免阶段:每经过 1 个 RTT,cwnd 加 1。
- 注意题目问的是”第几个 RTT 内发送的报文段都被确认之后”还是”第几个 RTT 内能发送多少”——cwnd 的增加都发生在收到确认之后。
同时给出 rwnd 与 cwnd
发送窗口 =
边界
错题复盘:超时后门限减半、窗口回到 1,第 4 个 RTT 已经进入拥塞避免
2009 年统考真题(王道 5.3.7 第 43 题):一个 TCP 连接总以 1KB 的最大段长发送 TCP 段,发送方有足够多的数据要发送,当拥塞窗口为 16KB 时发生了超时,若接下来的 4RTT 时间内的 TCP 段的传输都是成功的,则当第 4 个 RTT 时间内发送的所有 TCP 段都得到肯定应答时,拥塞窗口大小是(A. 7KB B. 8KB C. 9KB D. 16KB)。答案 C。
发生超时后,慢开始门限 ssthresh 变为
KB,拥塞窗口变为 1KB。在接下来的 3RTT 内执行慢开始算法,拥塞窗口大小依次为 2KB、4KB、8KB;因为慢开始门限 ssthresh 为 8KB,所以之后转而执行拥塞避免算法,即拥塞窗口开始”加法增大”。因此第 4 个 RTT 结束后,拥塞窗口的大小为 9KB。
RTT 第 1 个 第 2 个 第 3 个 第 4 个 本轮发送时的 cwnd 1KB 2KB 4KB 8KB 本轮全部被确认后的 cwnd 2KB 4KB 8KB 9KB 选 B 是算到 cwnd 达到门限就停了,漏掉最后一次加法增大;选 D 是以为超时后窗口不变。cwnd 的增加都发生在收到确认之后,所以”第 4 个 RTT 内发送的段都得到确认时”要算上那一次 +1。
错题复盘:发送窗口取 min(rwnd, cwnd),还要减去已发送未确认的部分
2010 年统考真题(王道 5.3.7 第 44 题):主机甲和主机乙之间已建立一个 TCP 连接,TCP 最大段长为 1000B。若主机甲的当前拥塞窗口为 4000B,在主机甲向主机乙连续发送两个最大段后,成功收到主机乙发送的第一个段的确认段,确认段中通告的接收窗口大小为 2000B,则此时主机甲还可以向主机乙发送的最大字节数是(A. 1000 B. 2000 C. 3000 D. 4000)。答案 A。
发送方的发送窗口的上限值取接收窗口和拥塞窗口这两个值中的较小一个,于是此时发送方的发送窗口为
B。因为确认段是对第一个段的确认,所以 2000B 的含义是甲发送第一个段后还能再发送 2000B;又因为之前甲连续发送了两个最大段,也就是说,第二个段还未收到确认,所以甲还能继续向乙发送的最大字节数是 B。 与 2021 年那道题(5.3.5)是同一个模板:窗口从确认号算起,已经发出去但还没被确认的部分要从窗口里扣掉。选 B 就是忘了扣。
判断拥塞的依据是”未按时收到确认”。 端系统看不到路由器的队列,只能用超时和冗余 ACK 推断。
超时把 cwnd 降到 1,3 个冗余 ACK 只把 cwnd 降到一半。 前者认为网络严重拥塞,后者认为报文段只是个别丢失。
ssthresh 取的是”出现拥塞时的 cwnd 的一半”,不是旧 ssthresh 的一半。
慢开始阶段的加倍不能越过门限。
拥塞避免不是”避免了拥塞”。 它只是让 cwnd 线性增长,使网络不容易拥塞。
发送窗口同时受 rwnd 和 cwnd 限制。 题目给了两个窗口时一定取较小者(2010、2014、2015、2016)。
口径差异:今天的 TCP 用的不是 Reno
教材口径:四种算法(慢开始、拥塞避免、快重传、快恢复)合起来就是 TCP 的拥塞控制,快恢复后 cwnd = ssthresh(图 5.11 中的 TCP Reno 版本,Tahoe 已废弃不用)。
工程口径:Linux 自 2.6.19 起默认使用 CUBIC,窗口按三次函数随时间增长,在高带宽长时延链路上比 Reno 激进得多;Windows 10 以后也改用了 CUBIC。标准 Reno 进入快恢复时还会把 cwnd 设为 ssthresh 加 3 个 MSS(补偿已经离开网络的 3 个报文段),退出快恢复时再收缩回 ssthresh。近年还有以时延和实际带宽为依据的 BBR。
考试按教材口径作答:快恢复后 cwnd = ssthresh = 原 cwnd 的一半。
对照速查
| 说法 | 对错 |
|---|---|
| 拥塞控制是全局性的,流量控制是端到端的 | ✅ |
| 拥塞窗口由接收方通过窗口字段通知发送方 | ❌(由发送方根据网络拥塞情况确定) |
| 发送窗口上限 = min(rwnd, cwnd) | ✅ |
| 慢开始的”慢”指 cwnd 增长速率慢 | ❌(指一开始注入的报文段少) |
| 慢开始阶段 cwnd 每个 RTT 加倍 | ✅ |
| 拥塞避免阶段 cwnd 每个 RTT 加 1 | ✅ |
| 超时后 ssthresh 设为原 ssthresh 的一半 | ❌(设为当时 cwnd 的一半) |
| 超时后 cwnd 置为 1 | ✅ |
| 收到 3 个冗余 ACK 后 cwnd 置为 1 | ❌(置为 ssthresh,即当前 cwnd 的一半) |
| 快重传要等超时计时器超时 | ❌ |
| 拥塞避免算法可以完全避免拥塞 | ❌ |
考点
- 拥塞控制 vs 流量控制;发送窗口 = min(rwnd, cwnd)(2010、2014、2015、2016)
- 慢开始:cwnd=1,每 RTT 加倍;门限 ssthresh(2014、2015、2017、2020、2023)
- 拥塞避免:每 RTT 加 1(加法增大)
- 超时:ssthresh = cwnd/2,cwnd = 1;3 个冗余 ACK:ssthresh = cwnd/2,cwnd = ssthresh
- 快重传的时机(2019);慢开始阶段加倍不越过门限
链接
- 🏠 返回总览:计算机网络第 5 章:传输层总览
- ⬅️ 上一节:5.3.5 TCP 流量控制
- 🔗 5.3.4 冗余 ACK 与快速重传
- 🔗 4.1.5 网络层的拥塞控制
- 📖 名词库:第 5 章名词库