TCP 拥塞控制详解:慢开始、拥塞避免、快重传与快恢复
本篇笔记仅讲解概念,实例分析放在下一篇笔记中
一、什么是 TCP 拥塞控制?
1. 拥塞控制的背景
在 TCP 通信过程中,发送方会不断向网络发送数据。如果发送速度过快,超过了网络中路由器、交换机等设备的处理能力,就可能导致网络中的数据包大量堆积
当网络设备的缓存空间被占满时,后续到达的数据包可能被丢弃,从而产生丢包、重传和延迟增加等问题
这种网络负载超过网络处理能力的现象,称为网络拥塞(Network Congestion)
例如:
发送方 A 网络设备 接收方 B
| | |
|—— 数据包 1 ——->|————————>|
|—— 数据包 2 ——->|————————>|
|—— 数据包 3 ——->| |
|—— 数据包 4 ——->| 缓存逐渐堆积 |
|—— 数据包 5 ——->| 缓存空间不足 |
| | 数据包丢失 |
| | |
|<———————– ACK / 确认信息 ———–|
如果发送方不考虑网络的承载能力,继续高速发送数据,就可能加剧网络拥塞。
因此,TCP 需要根据网络状况动态调整数据发送速度,这就是拥塞控制(Congestion Control)
2. 拥塞控制的核心思想
拥塞控制的核心思想是:
根据网络的拥塞程度动态调整发送方的拥塞窗口,控制发送数据的速度,避免网络出现严重拥塞。
TCP 主要通过四种算法实现拥塞控制:
慢开始(Slow Start)
拥塞避免(Congestion Avoidance)
快重传(Fast Retransmit)·
快恢复(Fast Recovery)
这四种算法并不是彼此独立的,而是协同工作,共同调节 TCP 的数据发送行为。
二、拥塞控制中的重要概念
在理解四种算法之前,需要先掌握三个重要概念。
1. 拥塞窗口(Congestion Window,cwnd)
拥塞窗口是发送方根据当前网络拥塞情况维护的一个窗口大小。
它反映了发送方认为当前网络能够承受的未确认数据量。
-
网络状况较好:可以适当增大拥塞窗口,提高发送速度。
-
网络出现拥塞:需要减小拥塞窗口,降低发送速度。
注意:cwnd 是发送方维护的,不是接收方维护的。
2. 接收窗口(Receive Window,rwnd)
接收窗口是接收方根据自身接收缓存情况通告给发送方的窗口大小。
它主要用于流量控制(Flow Control),防止发送方发送的数据超过接收方的处理能力。
3. 实际发送窗口
TCP 发送方实际能够使用的发送窗口,同时受到拥塞控制和流量控制的限制。
实际发送窗口 = min(cwnd, rwnd)
其中:
cwnd:拥塞窗口(Congestion Window)
rwnd:接收窗口(Receive Window)
例如:
cwnd = 10 KB
rwnd = 6 KB
实际发送窗口 = min(10 KB, 6 KB)
= 6 KB
虽然网络允许发送 10 KB,但接收方只能接收 6 KB,因此发送方最多只能发送 6 KB 的未确认数据。
面试注意:
-
拥塞控制(Congestion Control):考虑网络的承载能力
-
流量控制(Flow Control):考虑接收方的接收能力
三、慢开始(Slow Start)
1. 为什么需要慢开始?
TCP 连接刚建立时,发送方并不知道当前网络的承载能力。
如果一开始就发送大量数据,可能会导致网络突然出现拥塞。
因此,TCP 不会一开始就使用很大的拥塞窗口,而是从较小的窗口开始,根据收到的确认信息逐步增大窗口。
这种方式称为慢开始。
注意:慢开始并不意味着窗口增长速度很慢,而是指从较小的窗口开始探测网络承载能力。
2. 慢开始的工作原理
慢开始阶段,拥塞窗口按照指数规律增长。
在经典教材模型中:
-
初始拥塞窗口为较小值,示例中取 1 MSS。
-
每收到一个新的 ACK,cwnd 增加 1 MSS。
-
一个 RTT 内通常可以收到约 cwnd 个 ACK。
-
因此,每经过一个 RTT,拥塞窗口大致翻倍。
MSS(Maximum Segment Size)表示最大报文段长度。
假设初始拥塞窗口为 1 MSS:
RTT 0:cwnd = 1 MSS
RTT 1:cwnd = 2 MSS
RTT 2:cwnd = 4 MSS
RTT 3:cwnd = 8 MSS
RTT 4:cwnd = 16 MSS
3. 慢开始增长图
拥塞窗口(Congestion Window,cwnd)
|
16| ●
| /
| /
8| ●
| /
| /
4| ●
| /
2| ●
| /
1| ●
+——————————–> 时间(RTT)
0 1 2 3 4
指数增长(Exponential Growth)
可以看到,慢开始阶段的窗口增长速度越来越快。
4. 慢开始门限(Slow Start Threshold,ssthresh)
为了避免拥塞窗口无限制地指数增长,TCP 引入了一个阈值:
慢开始门限(ssthresh)。
它用于决定当前应该使用哪一种窗口增长方式:
-
当 cwnd < ssthresh 时,使用慢开始。
-
当 cwnd >= ssthresh 时,进入拥塞避免。
例如:
初始 cwnd = 1 MSS
ssthresh = 16 MSS
cwnd:1 → 2 → 4 → 8 → 16
达到 ssthresh 后,进入拥塞避免阶段。
注意:慢开始门限不是网络拥塞的绝对临界值,而是 TCP 用来切换增长策略的控制参数。
四、拥塞避免(Congestion Avoidance)
1. 为什么需要拥塞避免?
慢开始阶段采用指数增长,窗口增长速度非常快。
如果一直按照指数规律增长,可能很快超过网络的承载能力,引发拥塞。
因此,当拥塞窗口达到慢开始门限后,TCP 会降低窗口增长速度,进入拥塞避免阶段。
2. 拥塞避免的工作原理
拥塞避免阶段,拥塞窗口按照线性规律增长。
经典教材模型中:
每经过一个 RTT,拥塞窗口大约增加 1 MSS。
例如:
当前 cwnd = 16 MSS
RTT 1:17 MSS
RTT 2:18 MSS
RTT 3:19 MSS
RTT 4:20 MSS
它不再像慢开始一样每经过一个 RTT 就翻倍,而是缓慢增加。
从每个 ACK 的角度看,拥塞避免通常可以近似表示为:
每收到一个新 ACK:
cwnd += MSS * MSS / cwnd
在一个 RTT 内累计起来,窗口大约增加 1 MSS。
3. 拥塞避免增长图
拥塞窗口(Congestion Window,cwnd)
|
20| ●
19| ●
18| ●
17| ●
16| ●
|
+——————————–> 时间(RTT)
0 1 2 3 4
线性增长(Linear Growth)
4. 慢开始与拥塞避免的区别
| 增长规律 | 指数增长 | 线性增长 |
| 窗口变化 | 每 RTT 大致翻倍 | 每 RTT 大约增加 1 MSS |
| 进入条件 | 连接初期或超时恢复后 | cwnd >= ssthresh |
| 主要目的 | 探测网络承载能力 | 更谨慎地增加发送速度 |
可以记忆为:
慢开始:指数增长,快速探测网络能力。
拥塞避免:线性增长,谨慎增加发送速度。
五、快重传(Fast Retransmit)
1. 为什么需要快重传?
TCP 采用可靠传输机制,发送方发送数据后,需要接收方返回 ACK 确认。
如果发送的数据包丢失,发送方需要重新发送丢失的数据。
传统方式主要依靠超时重传(Retransmission Timeout,RTO):
发送方等待一段时间,如果仍然没有收到确认,就认为数据可能丢失,然后重新发送。
但这种方式存在一个问题:
如果数据实际上已经丢失,发送方仍然需要等待超时时间,才能进行重传。
这会增加通信延迟。
因此,TCP 引入了快重传机制。
2. 快重传的工作原理
快重传的核心思想:
发送方连续收到 3 个重复 ACK 时,就认为某个数据段可能丢失,不必等待超时,立即重传丢失的数据。
为什么会出现重复 ACK?
假设发送方连续发送 4 个数据段:
DATA 1
DATA 2
DATA 3
DATA 4
其中 DATA 2 丢失了。
接收方收到 DATA 1 后,正常确认 DATA 1。
但当接收方收到 DATA 3 和 DATA 4 时,由于 DATA 2 尚未收到,数据流中出现了缺口。
TCP 使用累计确认(Cumulative Acknowledgment),因此接收方仍然确认自己期望收到的下一个字节。
于是,接收方会连续返回相同的 ACK。
3. 快重传过程图
发送方(Sender) 接收方(Receiver)
| |
|——— DATA 1 —————–>|
|<——– ACK 2 ——————-|
| |
|——— DATA 2 —-X | 数据丢失
| |
|——— DATA 3 —————–>|
|<——– ACK 2 ——————-| 重复 ACK 1
| |
|——— DATA 4 —————–>|
|<——– ACK 2 ——————-| 重复 ACK 2
| |
|——— DATA 5 —————–>|
|<——– ACK 2 ——————-| 重复 ACK 3
| |
| 立即重传 DATA 2 |
|——— DATA 2(重传)———->|
| |
|<——– ACK 6 ——————-| 累计确认
| |
说明:
-
DATA 2 丢失。
-
接收方收到后续数据时,仍然返回 ACK 2。
-
发送方收到 3 个重复 ACK 后,触发快重传。
-
发送方不必等待 RTO 超时,立即重传 DATA 2。
-
接收方收到缺失数据后,可以一次性确认连续收到的数据。
图中 DATA 1、DATA 2 等是便于理解的简化编号。实际 TCP 的 ACK 确认的是字节序号,而不是数据段编号。
4. 快重传的优点
与传统超时重传相比:
-
不需要等待 RTO 超时。
-
能够更快地发现可能丢失的数据。
-
降低数据重传的等待时间。
-
提高 TCP 传输效率。
注意:经典 TCP Reno 通常使用 3 个重复 ACK 作为触发快重传的条件。现代 TCP 还可能结合其他丢包检测机制。
六、快恢复(Fast Recovery)
1. 为什么需要快恢复?
快重传解决了数据丢失后等待超时的问题。
但当发送方发现数据丢失时,也说明当前网络可能出现了拥塞。
因此,发送方需要降低发送速度。
如果每次收到 3 个重复 ACK 都直接将拥塞窗口降到 1 MSS,再重新进行慢开始,就可能过于保守。
所以,TCP Reno 引入了快恢复机制。
快恢复的核心思想:发生轻度拥塞时,降低拥塞窗口,但不必将窗口直接降到 1 MSS,而是从较大的窗口重新进入拥塞避免。
2. 快恢复的工作原理
以经典 TCP Reno 为例:
假设:
当前 cwnd = 16 MSS
FlightSize = 16 MSS
FlightSize 表示当前已经发送但尚未收到确认的数据量。
当发送方收到 3 个重复 ACK 时:
第一步:调整慢开始门限。
ssthresh = max(FlightSize / 2, 2 * MSS)
假设 FlightSize = 16 MSS
ssthresh = 8 MSS
第二步:调整拥塞窗口。
cwnd = ssthresh + 3 * MSS
cwnd = 8 + 3
= 11 MSS
这里加上 3 MSS,是因为已经收到 3 个重复 ACK,说明有后续数据到达接收方,网络中仍然存在一定的数据传输活动。
第三步:执行快重传。
立即重传丢失的数据段。
第四步:处理后续重复 ACK。
如果继续收到额外的重复 ACK,经典 Reno 会将 cwnd 再增加 1 MSS,并在满足发送条件时继续发送数据。
第五步:收到新的 ACK。
当发送方收到确认重传数据的新 ACK 后:
cwnd = ssthresh
此时:
cwnd = 8 MSS
随后进入拥塞避免阶段,按照线性规律增长。
3. 快恢复过程图
拥塞窗口(cwnd)
16 MSS ───────────────● 发生丢包
|
| 收到 3 个重复 ACK
|
11 MSS ● 快恢复(Fast Recovery)
|
| 收到新的 ACK
|
8 MSS ───────────────● 进入拥塞避免
|
| 线性增长
|
●
●
●
+——————–> 时间
注意:
经典 Reno 在第三个重复 ACK 到达时,临时将 cwnd 设置为 ssthresh + 3 MSS,收到新的 ACK 后再将其设置为 ssthresh。
因此,不能简单地说拥塞窗口从 16 MSS 直接变成 8 MSS,中间还有快恢复阶段的临时窗口调整。
七、超时重传与拥塞窗口调整
快重传主要针对能够通过重复 ACK 发现的数据丢失。
但如果发送方长时间没有收到 ACK,就可能触发超时重传(RTO)。
在经典 TCP Reno 教材模型中,超时通常被认为是更严重的拥塞信号。
假设:
原 cwnd = 20 MSS
FlightSize = 20 MSS
发生 RTO 超时后:
第一步:重新计算慢开始门限。
ssthresh = max(FlightSize / 2, 2 * MSS)
ssthresh = 10 MSS
第二步:将拥塞窗口降到 1 MSS。
cwnd = 1 MSS
第三步:重新进入慢开始。
1 → 2 → 4 → 8 → 10
达到 ssthresh 后,再进入拥塞避免阶段。
超时重传示意图
拥塞窗口(cwnd)
20 MSS ─────────────● 发生 RTO 超时
|
|
● 降至 1 MSS
1 MSS ─────────────●
|
| 慢开始(指数增长)
|
2 MSS ●
|
4 MSS ●
|
8 MSS ●
|
10 MSS ● 达到 ssthresh
|
| 拥塞避免(线性增长)
|
+——————–> 时间
八、四种算法如何协同工作?
前面分别介绍了四种算法,现在将它们联系起来。
TCP 拥塞控制的整体工作流程可以概括为:
TCP 连接建立
|
v
慢开始(Slow Start)
|
| cwnd 达到 ssthresh
v
拥塞避免(Congestion Avoidance)
|
| 发现数据丢失
v
判断丢失信号
/ \\
/ \\
3 个重复 ACK RTO 超时
| |
v v
快重传 ssthresh 减半
(Fast Retransmit) |
| v
v cwnd = 1 MSS
快恢复 |
(Fast Recovery) v
| 慢开始
v
cwnd = ssthresh
|
v
拥塞避免
注意:图中是经典 TCP Reno 的简化流程。实际 TCP 实现还可能使用选择确认(SACK)、更复杂的丢包恢复机制和不同的拥塞控制算法。
九、TCP Tahoe 与 TCP Reno 的区别
理解完四种算法后,还需要了解两种经典 TCP 拥塞控制方案。
1. TCP Tahoe
TCP Tahoe 是较早的一种拥塞控制方案。
它包含:
-
慢开始(Slow Start)
-
拥塞避免(Congestion Avoidance)
-
快重传(Fast Retransmit)
但没有快恢复机制。
无论是收到 3 个重复 ACK,还是发生 RTO 超时,Tahoe 都会将拥塞窗口重新设置为 1 MSS,然后重新慢开始。
2. TCP Reno
TCP Reno 在 Tahoe 的基础上增加了快恢复机制。
它包含四种核心算法:
-
慢开始
-
拥塞避免
-
快重传
-
快恢复
当收到 3 个重复 ACK 时,Reno 使用快重传和快恢复,避免拥塞窗口直接降到 1 MSS。
但发生 RTO 超时时,经典 Reno 仍然会将拥塞窗口降到 1 MSS,重新进行慢开始。
3. 两者对比
| 慢开始 | 支持 | 支持 |
| 拥塞避免 | 支持 | 支持 |
| 快重传 | 支持 | 支持 |
| 快恢复 | 不支持 | 支持 |
| 3 个重复 ACK | cwnd 降至 1 MSS | 进入快恢复 |
| RTO 超时 | cwnd 降至 1 MSS | cwnd 降至 1 MSS |
面试重点:
Tahoe 与 Reno 最重要的区别是:Reno 增加了快恢复机制。
十、现代 TCP 拥塞控制算法
前面介绍的慢开始、拥塞避免、快重传和快恢复,是理解传统 TCP 拥塞控制机制的重要基础。
但现代 TCP 实际使用的拥塞控制算法还包括 CUBIC、BBR 等。
1. CUBIC
CUBIC 是一种基于拥塞窗口变化的拥塞控制算法。
它使用三次函数调整拥塞窗口,在高带宽、长延迟网络中具有较好的性能。
Linux 系统中广泛使用 CUBIC。
2. BBR
BBR 是一种基于带宽和 RTT 估计的拥塞控制算法。
它主要通过估计:
-
瓶颈带宽(Bottleneck Bandwidth)
-
最小往返时延(Minimum RTT)
来调整数据发送速率。
与传统主要依靠丢包判断拥塞的算法不同,BBR 不将丢包作为主要拥塞信号,而是尝试根据网络的带宽和时延模型控制发送速率。
注意:BBR 并不是完全不处理丢包,而是其拥塞控制决策不单纯依赖丢包信号。
十一、面试高频考点
1. 什么是 TCP 拥塞控制?
TCP 拥塞控制是指发送方根据网络的拥塞情况动态调整拥塞窗口(cwnd),控制数据发送速度,避免网络出现严重拥塞,从而提高网络传输效率。
2. TCP 拥塞控制有哪些算法?
经典 TCP 拥塞控制主要包括四种算法:
慢开始:拥塞窗口指数增长。
拥塞避免:拥塞窗口线性增长。
快重传:收到 3 个重复 ACK 后,立即重传丢失的数据。
快恢复:发生轻度拥塞后,将窗口调整到较低水平,再进入拥塞避免。
3. 慢开始为什么叫慢开始?
慢开始并不是指增长速度慢,而是指发送方从较小的拥塞窗口开始,逐步探测网络承载能力。
其窗口在经典模型中按照指数规律增长,速度实际上比较快。
4. 慢开始和拥塞避免有什么区别?
慢开始采用指数增长,每经过一个 RTT,拥塞窗口大约翻倍。
拥塞避免采用线性增长,每经过一个 RTT,拥塞窗口大约增加 1 MSS。
5. 什么是慢开始门限?
慢开始门限(ssthresh)是 TCP 用来决定拥塞窗口增长策略的参数。
当 cwnd < ssthresh 时,使用慢开始;当 cwnd >= ssthresh 时,使用拥塞避免。
6. 快重传为什么需要 3 个重复 ACK?
当接收方收到失序数据时,会重复确认当前期望收到的下一个字节。
发送方连续收到 3 个重复 ACK,说明后续数据仍然能够到达接收方,但某个数据可能已经丢失。
因此,发送方可以推测发生了丢包,并立即进行重传,而不必等待超时。
7. 快重传和超时重传有什么区别?
快重传通过重复 ACK 发现可能丢失的数据,不需要等待 RTO。
超时重传则是在发送方等待确认超过 RTO 后,才执行重传。
8. 快恢复为什么不将拥塞窗口降到 1 MSS?
因为收到 3 个重复 ACK 时,说明网络中仍然有后续数据到达接收方,网络并非完全无法传输数据。
因此,经典 Reno 会将拥塞窗口调整到 ssthresh + 3 MSS,完成重传后再将窗口调整为
ssthresh,进入拥塞避免。
9. 拥塞控制和流量控制有什么区别?
拥塞控制考虑的是网络整体的承载能力,通过 cwnd 调整发送速度。
流量控制考虑的是接收方的接收能力,通过 rwnd 限制发送方的数据发送量。
实际发送窗口受二者共同限制:
发送窗口 = min(cwnd, rwnd)
10. TCP Tahoe 和 TCP Reno 有什么区别?
Tahoe 不支持快恢复,发生丢包后会将拥塞窗口降到 1 MSS。
Reno 增加了快恢复机制,收到 3 个重复 ACK 时可以通过快重传和快恢复,避免窗口直接降到 1 MSS。
十二、总结
建议重点理解的窗口变化过程
这一部分建议单独记忆,因为面试官很喜欢通过给定窗口数值,让你分析 TCP 的状态变化。
经典 TCP Reno 窗口变化示意
阶段一:慢开始
1 → 2 → 4 → 8 → 16
每经过一个 RTT,cwnd 约翻倍。
阶段二:拥塞避免
16 → 17 → 18 → 19 → 20
每经过一个 RTT,cwnd 约增加 1 MSS。
阶段三:收到 3 个重复 ACK
20 → 10(示意)
ssthresh 调整为 FlightSize 的一半,进入快恢复;这里省略了 Reno 的临时窗口膨胀细节。
阶段四:发生 RTO 超时
20 → 1
经典 Reno 将 cwnd 降到 1 MSS,重新进入慢开始。
这里有一个容易混淆的地方: 快恢复期间,Reno 的 cwnd 并不是直接从 20 变成 10。它先设置 ssthresh,再将 cwnd 临时设置为 ssthresh + 3 MSS,收到新 ACK 后才将 cwnd 设为 ssthresh。
容易出现的三个错误
把拥塞控制和流量控制混为一谈。 前者关注网络,后者关注接收方。
认为慢开始增长速度慢。 实际上慢开始是指数增长,增长速度很快。
认为快重传和快恢复是同一个算法。 快重传解决的是尽早重传丢失数据的问题,快恢复解决的是丢包后如何调整拥塞窗口的问题。
TCP 拥塞控制的核心是通过动态调整拥塞窗口(cwnd),控制发送方的数据发送速度。
四种经典算法各司其职:
-
慢开始(Slow Start):从较小窗口开始,指数增长,探测网络承载能力。
-
拥塞避免(Congestion Avoidance):达到慢开始门限后,线性增长,谨慎提高发送速度。
-
快重传(Fast Retransmit):收到 3 个重复 ACK 后,不等待超时,立即重传丢失的数据。
-
快恢复(Fast Recovery):发生轻度拥塞后,适当降低拥塞窗口,完成恢复后进入拥塞避免。
可以用一句话记忆整个过程:
先通过慢开始快速探测网络能力,再通过拥塞避免谨慎增加发送速度;发现丢包后,通过快重传及时补发数据,并利用快恢复调整拥塞窗口。若发生 RTO 超时,则认为拥塞情况较严重,需要将窗口降至较小值重新开始。



