计算机网络基础:TCP 的拥塞控制详解
TCP 的拥塞控制(Congestion Control)是 TCP 协议中最核心、最复杂的部分之一。
它的目标是:在不让网络崩溃的前提下,尽可能快地、公平地利用网络带宽。
简单一句话总结:
TCP 拥塞控制就是让发送方自己“感知”网络是否拥堵,然后动态调整发送速率(拥塞窗口 cwnd)。
一、为什么需要拥塞控制?(与流量控制的区别)
- 流量控制:防止接收方缓冲区被撑爆(靠滑动窗口、接收窗口 rwnd)
- 拥塞控制:防止网络中间路由器的缓冲区被撑爆(靠拥塞窗口 cwnd)
两者共同决定实际发送窗口:
实际发送窗口 = min(cwnd, rwnd)
如果没有拥塞控制,TCP 会像“疯狂发消息”一样把数据一股脑儿塞进网络,导致路由器丢包 → 全局拥塞崩溃。
二、TCP 拥塞控制的四大核心算法(现代主流 Reno 版本)
TCP 拥塞控制主要由以下四个经典算法组成(TCP Reno):
- 慢启动(Slow Start)
- 拥塞避免(Congestion Avoidance)
- 快速重传(Fast Retransmit)
- 快速恢复(Fast Recovery)
1. 慢启动(Slow Start)
核心思想:一开始别发太多,先小步试探网络,确认网络好就指数级加速。
- 初始时:拥塞窗口 cwnd = 1 MSS(Maximum Segment Size,通常 1460 字节)
- 每收到一个 ACK,cwnd += 1 MSS
- 每经过一个 RTT(往返时间),cwnd 翻倍(指数增长)
举例(假设 MSS=1 单位):
| 轮次 | cwnd | 发送包数 | 收到 ACK 后 cwnd 变化 |
|---|---|---|---|
| 0 | 1 | 发 1 个 | 收到 1 个 ACK → cwnd=2 |
| 1 | 2 | 发 2 个 | 收到 2 个 ACK → cwnd=4 |
| 2 | 4 | 发 4 个 | 收到 4 个 ACK → cwnd=8 |
| 3 | 8 | 发 8 个 | 收到 8 个 ACK → cwnd=16 |
增长规律:指数增长(非常快)
结束条件:
- cwnd ≥ ssthresh(慢启动门限,通常初始 64KB 或更大)
- 发生丢包(超时或收到 3 次重复 ACK)
2. 拥塞避免(Congestion Avoidance)
核心思想:已经试探出网络大致容量了,不能再指数疯涨了,要慢慢线性增长,避免直接撞墙。
- 当 cwnd ≥ ssthresh 时,进入拥塞避免阶段
- 每收到一个 ACK,cwnd += 1/cwnd(约等于每个 RTT 增加 1 MSS)
增长规律:线性增长(非常稳)
目的:缓慢逼近网络容量,避免突然拥塞
3. 丢包的两种检测方式
TCP 通过两种方式判断网络可能拥塞了:
| 方式 | 触发条件 | 严重程度 | 处理方式 |
|---|---|---|---|
| 超时重传 | RTO(重传超时)到期 | 严重 | 认为网络严重拥塞 |
| 快速重传 | 收到 3 个重复 ACK(3 duplicate ACK) | 较轻 | 只是个别包丢失,网络还活着 |
现代 TCP 优先用快速重传,因为超时通常意味着更严重的拥塞。
4. 快速重传(Fast Retransmit)
核心思想:别傻等超时!收到 3 次重复 ACK 就知道某个包丢了,马上重传它。
- 正常 ACK 是累计确认
- 收到 3 次相同的重复 ACK → 立即重传那个 seq 号的包(不需要等超时)
极大减少了等待时间,提高效率
5. 快速恢复(Fast Recovery)—— Reno 的关键改进
核心思想:既然只是丢了一个包,网络没完全崩,不用从头慢启动,稍微降一点速度继续发。
快速恢复算法(TCP Reno)流程:
- 收到 3 次重复 ACK
- ssthresh = cwnd / 2(把慢启动门限减半)
- cwnd = ssthresh + 3(加 3 是因为已经收到了 3 个重复 ACK,说明这 3 个包已经出队了)
- 进入快速恢复状态:每收到一个新的重复 ACK,cwnd += 1(允许继续发新包)
- 当收到新 ACK(确认了重传的包):
- cwnd = ssthresh
- 退出快速恢复,回到拥塞避免阶段(线性增长)
对比 TCP Tahoe(更老的版本):
- Tahoe:收到 3 次重复 ACK → 直接 cwnd=1,ssthresh=cwnd/2,进入慢启动(太保守)
- Reno:快速恢复后直接进入拥塞避免(更激进,性能更好)
三、拥塞控制状态机简图(Reno)
新连接
↓ cwnd=1
慢启动(指数增长)
↓
cwnd ≥ ssthresh
↓
拥塞避免(线性增长)
↓
两种丢包事件:
├── 超时(严重)
│ → ssthresh = cwnd/2
│ → cwnd = 1
│ → 回到慢启动
└── 收到 3 次重复 ACK(较轻)
→ ssthresh = cwnd/2
→ cwnd = ssthresh + 3(快速恢复)
→ 每多一个重复 ACK,cwnd +=1
→ 收到新 ACK → cwnd = ssthresh
→ 回到拥塞避免
四、现代改进(简单了解)
- TCP New Reno:改进快速恢复,能处理一个窗口内多个包丢失
- TCP SACK:Selective ACK,选择性确认,告诉发送方哪些包收到了
- TCP Cubic / BBR(Google):更现代的算法,2020年后很多系统默认使用,适应高带宽大时延网络(BBR 特别适合长肥管道)
五、总结一句话
TCP 拥塞控制的核心策略:
- 慢启动 → 指数试探网络容量
- 拥塞避免 → 线性平稳逼近容量
- 快速重传 + 快速恢复 → 轻度丢包时快速响应,不必从头慢启动
一句话口诀:
“慢启动指数疯,拥塞避免慢慢升;三重复快重传,快速恢复别太狠。”
如果你想看具体数字示例、cwnd 变化曲线图、Reno vs Tahoe 对比、或 BBR 的原理,也可以继续问我,我再给你画得更细~