TCP 流量控制与拥塞控制
作者:[你的 CSDN 昵称] | 专注于后端开发和网络协议研究,欢迎关注交流!
标签:
TCP网络协议拥塞控制流量控制计算机网络
前言
TCP(Transmission Control Protocol,传输控制协议)是互联网最核心的可靠传输协议。为了保证数据传输的可靠性与效率,TCP 设计了两套互补的控制机制:流量控制 与拥塞控制。
很多人会混淆这两个机制,实际上它们解决的是完全不同的问题:
| 机制 | 解决的问题 | 作用范围 |
|---|---|---|
| 流量控制 | 发送方太快,接收方处理不过来 | 端到端(两个端点) |
| 拥塞控制 | 发送方太快,中间网络扛不住 | 全局(整个网络) |
本文将从基础到深入,完整梳理这两个机制的工作原理、核心逻辑与协同规则,帮你彻底搞懂 TCP 的速度控制体系。
一、TCP 连接的基础:三次握手与四次挥手
在进入流量控制与拥塞控制之前,先简单回顾 TCP 连接的建立与断开过程,这是一切传输的前提。
1.1 三次握手(建立连接)
客户端 服务端
│ ──── SYN (seq=x) ──────────> │ ① 客户端请求连接
│ <── SYN+ACK (seq=y,ack=x+1) ──│ ② 服务端确认并同步
│ ──── ACK (ack=y+1) ─────────> │ ③ 客户端最终确认
│ │
│ [ 数据传输 ] │
为什么需要三次? 两次握手无法让双方都确认自己的发送/接收能力。三次握手能防止历史连接报文被复用,避免资源浪费。
1.2 四次挥手(断开连接)
客户端 服务端
│ ──── FIN ─────────────────> │ ① 客户端停止发送数据
│ <──── ACK ───────────────── │ ② 服务端确认
│ (服务端处理剩余数据中...) │
│ <──── FIN ───────────────── │ ③ 服务端停止发送数据
│ ──── ACK ─────────────────> │ ④ 客户端最终确认
│ (等待 2MSL 后彻底关闭) │
为什么需要四次? 因为 TCP 是全双工的,两个方向的数据流需要独立关闭,服务端在收到 FIN 后可能还有数据要发,所以不能合并 ACK 和 FIN。
为什么等 2MSL? 确保最后一个 ACK 能顺利到达服务端,防止旧连接报文干扰后续新连接。
二、流量控制:端到端的速度匹配
2.1 核心目标
流量控制解决的是发送方与接收方之间的速度匹配问题:
如果发送方发送数据的速度远超接收方处理数据的速度,接收方的接收缓冲区会被迅速填满,导致新来的数据被丢弃,引发不必要的重传,降低传输效率。
本质 :让发送方的发送速度,匹配接收方的接收能力。
它是端到端的机制,只关心发送方和接收方这两个端点,与中间的网络状态无关。
2.2 核心变量:接收窗口 rwnd
TCP 头部有一个 16 位的「窗口字段」,是流量控制的核心载体:
- 接收方每次返回 ACK 时,都会在窗口字段中告诉发送方:我当前的接收缓冲区还能容纳多少字节 ,这个值就是接收窗口(rwnd,Receiver Window)。
- 接收方会根据自己的处理速度,动态更新 rwnd:处理得快 → rwnd 变大;处理不过来 → rwnd 变小。
2.3 工作原理:滑动窗口通告
发送方严格遵守接收方的通告,最多只能发送不超过 rwnd 大小的数据:
① 接收方通告 rwnd = 1000 字节
② 发送方最多连续发 1000 字节,之后必须等 ACK
③ 接收方处理完 500 字节后,返回 ACK,通告 rwnd = 500
④ 发送方继续发 500 字节,如此循环
滑动窗口示意图
发送缓冲区:
┌──────────┬──────────────┬────────────┬────────────┐
│ 已发已确认 │ 已发未确认 │ 可发未发 │ 暂不可发 │
└──────────┴──────────────┴────────────┴────────────┘
│←── 发送窗口 ──>│
窗口大小 = min(rwnd, cwnd)
2.4 零窗口问题与探测机制
当接收方缓冲区被塞满时,会通告 rwnd = 0,发送方被迫停止发送。此时若接收方处理完数据后的窗口更新报文丢失,双方将陷入死锁。
解决方案 :发送方收到零窗口通告后,启动持续定时器(Persist Timer) ,周期性发送零窗口探测报文(ZWP,Zero Window Probe),强制接收方回应当前窗口大小,打破死锁。
发送方 接收方
│ <── ACK (rwnd=0) ─────────── │ 接收方缓冲区满
│ ──── ZWP 探测 ─────────────> │ 发送方定期探测
│ <── ACK (rwnd=0) ─────────── │ 仍然满
│ ──── ZWP 探测 ─────────────> │
│ <── ACK (rwnd=500) ────────── │ 缓冲区空出,恢复传输
2.5 常见问题:糊涂窗口综合征
如果接收方处理速度极慢,每次只处理几字节就立即通告小窗口,会导致:发送方每次只能发几字节的小包,网络中充斥大量小报文,每个都携带完整的 TCP/IP 头,传输效率极低------这就是糊涂窗口综合征(Silly Window Syndrome)。
解决方案:
| 方案 | 作用方 | 核心策略 |
|---|---|---|
| Clark 算法 | 接收方 | 不要一有空间就通告小窗口,等到缓冲区空出至少一个 MSS 或全空再通告 |
| Nagle 算法 | 发送方 | 数据攒到一定量再发,避免小包泛滥(与 TCP_NODELAY 互斥) |
三、拥塞控制:整个网络的拥塞防护
3.1 核心目标
拥塞控制解决的是整个网络的拥塞问题:
如果所有发送方都不加限制地猛发数据,中间路由器的队列会很快被塞满,导致大量数据包被丢弃,引发全局重传风暴,最终整个网络吞吐量归零------这就是网络拥塞崩溃。
本质 :让所有发送方的发送速度,匹配整个网络的承载能力。
它是全局机制,关心的是整个网络的状态,而不只是两个端点。
3.2 核心变量
| 变量 | 全称 | 含义 |
|---|---|---|
| MSS | Maximum Segment Size | 最大报文段长度,TCP 一次能发送的最大单个数据块(通常为 1460 字节),是调整窗口的最小单位 |
| cwnd | Congestion Window | 拥塞窗口,发送方根据网络拥塞程度动态调整的最大发送量,代表当前网络最多能承载多少数据 |
| ssthresh | Slow Start Threshold | 慢启动门限,「指数增长」与「线性增长」的分界线 |
| RTT | Round-Trip Time | 往返时延,一个包从发出到收到 ACK 的时间,是 TCP 感知网络状态的时间单位 |
3.3 四大核心算法(TCP Reno)
传统 TCP 拥塞控制(Reno 版本)由四个核心算法组成:
3.3.1 慢启动(Slow Start):快速试探网络带宽
触发条件:连接刚建立,或发生超时重传后。
增长规则 :每收到 1 个 ACK,cwnd 增加 1 个 MSS → 每经过 1 个 RTT,cwnd 翻倍(指数增长)。
RTT 1:发 1 个包,收 1 个 ACK → cwnd = 2
RTT 2:发 2 个包,收 2 个 ACK → cwnd = 4
RTT 3:发 4 个包,收 4 个 ACK → cwnd = 8
......直到 cwnd ≥ ssthresh,切换为拥塞避免
初始窗口:旧标准为 1 MSS;RFC 3390 推荐初始为 2~4 MSS,适配现代宽带环境。
为什么叫「慢」启动?
这里的「慢」是相对于「一开始就把窗口开满」的激进做法而言的------它从极小的速度开始慢慢试探,而不是一上来就猛发,本质是「先慢后快的试探」,而非增长速度慢。
3.3.2 拥塞避免(Congestion Avoidance):稳定线性增长
触发条件 :cwnd ≥ ssthresh。
增长规则 :每经过 1 个 RTT,cwnd 只 +1 MSS(线性增长,加法递增),谨慎探测网络上限。
cwnd(n+1) = cwnd(n) + MSS / cwnd(n) × MSS // 每个 ACK 增加约 1/cwnd 个 MSS
每个 RTT 累计:cwnd ≈ cwnd + 1 MSS
3.3.3 快速重传(Fast Retransmit):不等超时的丢包修复
触发条件 :发送方连续收到 3 个重复 ACK。
传统超时重传需要等待 RTO(通常 200ms~1s)才重传,延迟过高。快速重传收到 3 个重复 ACK 后立即重传丢失的包,无需等待超时。
为什么是 3 个?
1~2 个重复 ACK 可能只是网络乱序,3 个重复 ACK 则可基本确认该报文段确实丢失。
效果:把丢包等待时间从几百 ms 压缩到几个 RTT,TCP 吞吐量可提升约 20%。
3.3.4 快速恢复(Fast Recovery):丢包后的快速重建
触发条件:快速重传被触发后(连续 3 个重复 ACK)。
能收到 3 个重复 ACK,说明网络没有完全堵死(严重拥塞时连 ACK 都传不回来),只是个别包丢失,没必要回退到慢启动。
处理流程(RFC 5681 标准):
① ssthresh = cwnd / 2 // 安全门限砍半
② cwnd = ssthresh + 3 × MSS // +3 是因为 3 个包已被成功接收,空出了 3 个位置
③ 每再收到 1 个重复 ACK:cwnd += 1 MSS // 又有 1 个包离开网络,空出位置
④ 收到丢失包的新 ACK:cwnd = ssthresh,进入拥塞避免
3.4 两种丢包的处理对比
TCP 根据丢包的严重程度做出截然不同的响应:
| 维度 | 超时重传(严重拥塞) | 快速重传(轻微丢包) |
|---|---|---|
| 触发条件 | RTO 计时器到期,长时间无 ACK | 连续收到 3 个重复 ACK |
| 网络状态 | 网络彻底拥塞,路由器队列满,ACK 无法传回 | 网络基本通畅,仅个别包丢失 |
| ssthresh | ssthresh = cwnd / 2 |
ssthresh = cwnd / 2 |
| cwnd | 重置为 1,重新慢启动 | 设为 ssthresh + 3,进入快速恢复 |
| 形象比喻 | 路堵死了,先寄 1 个快递探探路 | 路是通的,只是丢了 1 个快递,补发就行 |
3.5 拥塞窗口完整变化过程
cwnd (MSS)
↑
24 │ ●
23 │ ●
22 │ ●
20 │ ● ●
18 │ ● ●
16 │ ● ● ←(触发快恢复) ● ● ● ● ● ● ●
14 │ ─ ─ ─ ─ ─ ─ ─ ─(ssthresh=12)─ ─ ─ ─ ─
12 │ ● ←(超时重传)
8 │ ●
4 │ ●
2 │●
1 │○ ←初始
└─────────────────────────────────────────────────────→ 时间(RTT)
慢启动 → 拥塞避免 → 快恢复 → 拥塞避免 → 超时 → 慢启动 → 拥塞避免
● 拥塞窗口 cwnd ─ 慢启动门限 ssthresh
阶段说明:
- 慢启动 :
cwnd从 1 开始指数增长,直到触及ssthresh - 拥塞避免 :
cwnd线性增长,每 RTT +1 MSS - 快速恢复 :收到 3 个重复 ACK,
cwnd减半 +3,继续传输 - 超时重置 :RTO 到期,
cwnd重置为 1,重新慢启动
四、两者的协同工作
流量控制和拥塞控制不是独立工作的,共同决定发送方的实际发送速度:
实际发送窗口=min(rwnd, cwnd) \text{实际发送窗口} = \min(rwnd,\ cwnd) 实际发送窗口=min(rwnd, cwnd)
协同场景示例
| 场景 | rwnd | cwnd | 实际窗口 | 起限制作用的机制 |
|---|---|---|---|---|
| 网络拥塞,接收方处理能力强 | 100 | 50 | 50 | 拥塞控制 |
| 网络畅通,接收方处理能力弱 | 30 | 100 | 30 | 流量控制 |
| 两者均受限 | 40 | 40 | 40 | 两者共同作用 |
通过这种方式,TCP 同时保证了:
- 不会把接收方的缓冲区撑爆(流量控制)
- 不会把中间的网络堵死(拥塞控制)
五、流量控制 vs 拥塞控制:核心区别
| 对比维度 | 流量控制 | 拥塞控制 |
|---|---|---|
| 核心目标 | 避免接收方缓冲区溢出 | 避免网络拥塞崩溃 |
| 作用对象 | 端到端(发送方 ↔ 接收方) | 全局(整个网络路径) |
| 核心变量 | 接收窗口 rwnd(接收方通告) | 拥塞窗口 cwnd、ssthresh(发送方维护) |
| 解决问题 | 接收方处理不过来,导致端点丢包 | 网络负载过高,导致全局拥塞 |
| 调整依据 | 接收缓冲区剩余空间 | 网络丢包情况、RTT 变化 |
| 感知来源 | 接收方主动通告 | 发送方被动感知(ACK、超时) |
六、TCP 拥塞控制算法的演进
随着网络环境的变化,TCP 拥塞控制算法也在持续演进:
| 算法版本 | 提出时间 | 核心特点 |
|---|---|---|
| TCP Tahoe | 1988 | 慢启动 + 拥塞避免 + 快速重传,丢包后 cwnd 重置为 1 |
| TCP Reno | 1990 | 在 Tahoe 基础上增加快速恢复,避免回到慢启动 |
| TCP NewReno | 1999 | 改进快速恢复,正确处理一个窗口内多个丢包 |
| TCP SACK | 2001 | 选择性确认,精确告知丢失的报文段,减少不必要重传 |
| TCP CUBIC | 2008 | Linux 默认算法,基于时间的三次函数增长,适合高带宽长延迟网络 |
| TCP BBR | 2016 | Google 提出,基于带宽和 RTT 建模,不依赖丢包信号,大幅提升利用率 |
BBR vs 传统算法:传统算法把丢包当做拥塞信号,而 BBR 直接测量网络的最大带宽(BtlBw)和最小 RTT(RTprop),从根本上避免了不必要的降速。在高丢包率的网络(如卫星、移动网络)中,BBR 的吞吐量优势尤为明显。
七、总结
TCP 通过两套互补的机制,实现了可靠且高效的数据传输:
┌─────────────────────────────────┐
│ TCP 速度控制体系 │
└──────────────┬──────────────────┘
│
┌────────────────────┴─────────────────────┐
↓ ↓
┌──────────────────┐ ┌─────────────────────┐
│ 流量控制 │ │ 拥塞控制 │
│ (端到端保护) │ │ (全局网络保护) │
│ │ │ │
│ 核心:rwnd │ │ 核心:cwnd │
│ 来源:接收方通告 │ │ 算法:慢启动 │
│ 目的:保护接收方 │ │ 拥塞避免 │
└──────────────────┘ │ 快速重传 │
│ 快速恢复 │
└─────────────────────┘
↓
实际发送窗口 = min(rwnd, cwnd)
↙ ↘
不撑爆接收方缓冲区 不堵死中间网络
一句话总结:
TCP 用流量控制 管「端点」------保证接收方能接住你发的数据;用拥塞控制管「网络」------保证中间网络能扛住所有人发的数据。两者共同限制实际发送窗口,既保证了端到端的传输效率,又维护了整个网络的稳定,这也是 TCP 成为互联网最核心传输协议的关键所在。
参考资料
- RFC 5681 - TCP Congestion Control
- RFC 3390 - Increasing TCP's Initial Window
- RFC 6582 - The NewReno Modification to TCP's Fast Recovery Algorithm
- 《计算机网络:自顶向下方法》(Kurose & Ross)
- 《TCP/IP 详解 卷1:协议》(W. Richard Stevens)
- Google BBR 论文:BBR: Congestion-Based Congestion Control
如有问题或建议,欢迎评论区交流!觉得有帮助的话,点个赞和收藏支持一下 😊# TCP 流量控制与拥塞控制完整指南
作者:[你的 CSDN 昵称] | 专注于后端开发和网络协议研究,欢迎关注交流!标签:
TCP网络协议拥塞控制流量控制计算机网络
前言
TCP(Transmission Control Protocol,传输控制协议)是互联网最核心的可靠传输协议。为了保证数据传输的可靠性与效率,TCP 设计了两套互补的控制机制:流量控制 与拥塞控制。
很多人会混淆这两个机制,实际上它们解决的是完全不同的问题:
| 机制 | 解决的问题 | 作用范围 |
|---|---|---|
| 流量控制 | 发送方太快,接收方处理不过来 | 端到端(两个端点) |
| 拥塞控制 | 发送方太快,中间网络扛不住 | 全局(整个网络) |
本文将从基础到深入,完整梳理这两个机制的工作原理、核心逻辑与协同规则,帮你彻底搞懂 TCP 的速度控制体系。
一、TCP 连接的基础:三次握手与四次挥手
在进入流量控制与拥塞控制之前,先简单回顾 TCP 连接的建立与断开过程,这是一切传输的前提。
1.1 三次握手(建立连接)
客户端 服务端
│ ──── SYN (seq=x) ──────────> │ ① 客户端请求连接
│ <── SYN+ACK (seq=y,ack=x+1) ──│ ② 服务端确认并同步
│ ──── ACK (ack=y+1) ─────────> │ ③ 客户端最终确认
│ │
│ [ 数据传输 ] │
为什么需要三次? 两次握手无法让双方都确认自己的发送/接收能力。三次握手能防止历史连接报文被复用,避免资源浪费。
1.2 四次挥手(断开连接)
客户端 服务端
│ ──── FIN ─────────────────> │ ① 客户端停止发送数据
│ <──── ACK ───────────────── │ ② 服务端确认
│ (服务端处理剩余数据中...) │
│ <──── FIN ───────────────── │ ③ 服务端停止发送数据
│ ──── ACK ─────────────────> │ ④ 客户端最终确认
│ (等待 2MSL 后彻底关闭) │
为什么需要四次? 因为 TCP 是全双工的,两个方向的数据流需要独立关闭,服务端在收到 FIN 后可能还有数据要发,所以不能合并 ACK 和 FIN。
为什么等 2MSL? 确保最后一个 ACK 能顺利到达服务端,防止旧连接报文干扰后续新连接。
二、流量控制:端到端的速度匹配
2.1 核心目标
流量控制解决的是发送方与接收方之间的速度匹配问题:
如果发送方发送数据的速度远超接收方处理数据的速度,接收方的接收缓冲区会被迅速填满,导致新来的数据被丢弃,引发不必要的重传,降低传输效率。
本质 :让发送方的发送速度,匹配接收方的接收能力。
它是端到端的机制,只关心发送方和接收方这两个端点,与中间的网络状态无关。
2.2 核心变量:接收窗口 rwnd
TCP 头部有一个 16 位的「窗口字段」,是流量控制的核心载体:
- 接收方每次返回 ACK 时,都会在窗口字段中告诉发送方:我当前的接收缓冲区还能容纳多少字节 ,这个值就是接收窗口(rwnd,Receiver Window)。
- 接收方会根据自己的处理速度,动态更新 rwnd:处理得快 → rwnd 变大;处理不过来 → rwnd 变小。
2.3 工作原理:滑动窗口通告
发送方严格遵守接收方的通告,最多只能发送不超过 rwnd 大小的数据:
① 接收方通告 rwnd = 1000 字节
② 发送方最多连续发 1000 字节,之后必须等 ACK
③ 接收方处理完 500 字节后,返回 ACK,通告 rwnd = 500
④ 发送方继续发 500 字节,如此循环
滑动窗口示意图
发送缓冲区:
┌──────────┬──────────────┬────────────┬────────────┐
│ 已发已确认 │ 已发未确认 │ 可发未发 │ 暂不可发 │
└──────────┴──────────────┴────────────┴────────────┘
│←── 发送窗口 ──>│
窗口大小 = min(rwnd, cwnd)
2.4 零窗口问题与探测机制
当接收方缓冲区被塞满时,会通告 rwnd = 0,发送方被迫停止发送。此时若接收方处理完数据后的窗口更新报文丢失,双方将陷入死锁。
解决方案 :发送方收到零窗口通告后,启动持续定时器(Persist Timer) ,周期性发送零窗口探测报文(ZWP,Zero Window Probe),强制接收方回应当前窗口大小,打破死锁。
发送方 接收方
│ <── ACK (rwnd=0) ─────────── │ 接收方缓冲区满
│ ──── ZWP 探测 ─────────────> │ 发送方定期探测
│ <── ACK (rwnd=0) ─────────── │ 仍然满
│ ──── ZWP 探测 ─────────────> │
│ <── ACK (rwnd=500) ────────── │ 缓冲区空出,恢复传输
2.5 常见问题:糊涂窗口综合征
如果接收方处理速度极慢,每次只处理几字节就立即通告小窗口,会导致:发送方每次只能发几字节的小包,网络中充斥大量小报文,每个都携带完整的 TCP/IP 头,传输效率极低------这就是糊涂窗口综合征(Silly Window Syndrome)。
解决方案:
| 方案 | 作用方 | 核心策略 |
|---|---|---|
| Clark 算法 | 接收方 | 不要一有空间就通告小窗口,等到缓冲区空出至少一个 MSS 或全空再通告 |
| Nagle 算法 | 发送方 | 数据攒到一定量再发,避免小包泛滥(与 TCP_NODELAY 互斥) |
三、拥塞控制:整个网络的拥塞防护
3.1 核心目标
拥塞控制解决的是整个网络的拥塞问题:
如果所有发送方都不加限制地猛发数据,中间路由器的队列会很快被塞满,导致大量数据包被丢弃,引发全局重传风暴,最终整个网络吞吐量归零------这就是网络拥塞崩溃。
本质 :让所有发送方的发送速度,匹配整个网络的承载能力。
它是全局机制,关心的是整个网络的状态,而不只是两个端点。
3.2 核心变量
| 变量 | 全称 | 含义 |
|---|---|---|
| MSS | Maximum Segment Size | 最大报文段长度,TCP 一次能发送的最大单个数据块(通常为 1460 字节),是调整窗口的最小单位 |
| cwnd | Congestion Window | 拥塞窗口,发送方根据网络拥塞程度动态调整的最大发送量,代表当前网络最多能承载多少数据 |
| ssthresh | Slow Start Threshold | 慢启动门限,「指数增长」与「线性增长」的分界线 |
| RTT | Round-Trip Time | 往返时延,一个包从发出到收到 ACK 的时间,是 TCP 感知网络状态的时间单位 |
3.3 四大核心算法(TCP Reno)
传统 TCP 拥塞控制(Reno 版本)由四个核心算法组成:
3.3.1 慢启动(Slow Start):快速试探网络带宽
触发条件:连接刚建立,或发生超时重传后。
增长规则 :每收到 1 个 ACK,cwnd 增加 1 个 MSS → 每经过 1 个 RTT,cwnd 翻倍(指数增长)。
RTT 1:发 1 个包,收 1 个 ACK → cwnd = 2
RTT 2:发 2 个包,收 2 个 ACK → cwnd = 4
RTT 3:发 4 个包,收 4 个 ACK → cwnd = 8
......直到 cwnd ≥ ssthresh,切换为拥塞避免
初始窗口:旧标准为 1 MSS;RFC 3390 推荐初始为 2~4 MSS,适配现代宽带环境。
为什么叫「慢」启动?
这里的「慢」是相对于「一开始就把窗口开满」的激进做法而言的------它从极小的速度开始慢慢试探,而不是一上来就猛发,本质是「先慢后快的试探」,而非增长速度慢。
3.3.2 拥塞避免(Congestion Avoidance):稳定线性增长
触发条件 :cwnd ≥ ssthresh。
增长规则 :每经过 1 个 RTT,cwnd 只 +1 MSS(线性增长,加法递增),谨慎探测网络上限。
cwnd(n+1) = cwnd(n) + MSS / cwnd(n) × MSS // 每个 ACK 增加约 1/cwnd 个 MSS
每个 RTT 累计:cwnd ≈ cwnd + 1 MSS
3.3.3 快速重传(Fast Retransmit):不等超时的丢包修复
触发条件 :发送方连续收到 3 个重复 ACK。
传统超时重传需要等待 RTO(通常 200ms~1s)才重传,延迟过高。快速重传收到 3 个重复 ACK 后立即重传丢失的包,无需等待超时。
为什么是 3 个?
1~2 个重复 ACK 可能只是网络乱序,3 个重复 ACK 则可基本确认该报文段确实丢失。
效果:把丢包等待时间从几百 ms 压缩到几个 RTT,TCP 吞吐量可提升约 20%。
3.3.4 快速恢复(Fast Recovery):丢包后的快速重建
触发条件:快速重传被触发后(连续 3 个重复 ACK)。
能收到 3 个重复 ACK,说明网络没有完全堵死(严重拥塞时连 ACK 都传不回来),只是个别包丢失,没必要回退到慢启动。
处理流程(RFC 5681 标准):
① ssthresh = cwnd / 2 // 安全门限砍半
② cwnd = ssthresh + 3 × MSS // +3 是因为 3 个包已被成功接收,空出了 3 个位置
③ 每再收到 1 个重复 ACK:cwnd += 1 MSS // 又有 1 个包离开网络,空出位置
④ 收到丢失包的新 ACK:cwnd = ssthresh,进入拥塞避免
3.4 两种丢包的处理对比
TCP 根据丢包的严重程度做出截然不同的响应:
| 维度 | 超时重传(严重拥塞) | 快速重传(轻微丢包) |
|---|---|---|
| 触发条件 | RTO 计时器到期,长时间无 ACK | 连续收到 3 个重复 ACK |
| 网络状态 | 网络彻底拥塞,路由器队列满,ACK 无法传回 | 网络基本通畅,仅个别包丢失 |
| ssthresh | ssthresh = cwnd / 2 |
ssthresh = cwnd / 2 |
| cwnd | 重置为 1,重新慢启动 | 设为 ssthresh + 3,进入快速恢复 |
| 形象比喻 | 路堵死了,先寄 1 个快递探探路 | 路是通的,只是丢了 1 个快递,补发就行 |
3.5 拥塞窗口完整变化过程
cwnd (MSS)
↑
24 │ ●
23 │ ●
22 │ ●
20 │ ● ●
18 │ ● ●
16 │ ● ● ←(触发快恢复) ● ● ● ● ● ● ●
14 │ ─ ─ ─ ─ ─ ─ ─ ─(ssthresh=12)─ ─ ─ ─ ─
12 │ ● ←(超时重传)
8 │ ●
4 │ ●
2 │●
1 │○ ←初始
└─────────────────────────────────────────────────────→ 时间(RTT)
慢启动 → 拥塞避免 → 快恢复 → 拥塞避免 → 超时 → 慢启动 → 拥塞避免
● 拥塞窗口 cwnd ─ 慢启动门限 ssthresh
阶段说明:
- 慢启动 :
cwnd从 1 开始指数增长,直到触及ssthresh - 拥塞避免 :
cwnd线性增长,每 RTT +1 MSS - 快速恢复 :收到 3 个重复 ACK,
cwnd减半 +3,继续传输 - 超时重置 :RTO 到期,
cwnd重置为 1,重新慢启动
四、两者的协同工作
流量控制和拥塞控制不是独立工作的,共同决定发送方的实际发送速度:
实际发送窗口=min(rwnd, cwnd) \text{实际发送窗口} = \min(rwnd,\ cwnd) 实际发送窗口=min(rwnd, cwnd)
协同场景示例
| 场景 | rwnd | cwnd | 实际窗口 | 起限制作用的机制 |
|---|---|---|---|---|
| 网络拥塞,接收方处理能力强 | 100 | 50 | 50 | 拥塞控制 |
| 网络畅通,接收方处理能力弱 | 30 | 100 | 30 | 流量控制 |
| 两者均受限 | 40 | 40 | 40 | 两者共同作用 |
通过这种方式,TCP 同时保证了:
- 不会把接收方的缓冲区撑爆(流量控制)
- 不会把中间的网络堵死(拥塞控制)
五、流量控制 vs 拥塞控制:核心区别
| 对比维度 | 流量控制 | 拥塞控制 |
|---|---|---|
| 核心目标 | 避免接收方缓冲区溢出 | 避免网络拥塞崩溃 |
| 作用对象 | 端到端(发送方 ↔ 接收方) | 全局(整个网络路径) |
| 核心变量 | 接收窗口 rwnd(接收方通告) | 拥塞窗口 cwnd、ssthresh(发送方维护) |
| 解决问题 | 接收方处理不过来,导致端点丢包 | 网络负载过高,导致全局拥塞 |
| 调整依据 | 接收缓冲区剩余空间 | 网络丢包情况、RTT 变化 |
| 感知来源 | 接收方主动通告 | 发送方被动感知(ACK、超时) |
六、TCP 拥塞控制算法的演进
随着网络环境的变化,TCP 拥塞控制算法也在持续演进:
| 算法版本 | 提出时间 | 核心特点 |
|---|---|---|
| TCP Tahoe | 1988 | 慢启动 + 拥塞避免 + 快速重传,丢包后 cwnd 重置为 1 |
| TCP Reno | 1990 | 在 Tahoe 基础上增加快速恢复,避免回到慢启动 |
| TCP NewReno | 1999 | 改进快速恢复,正确处理一个窗口内多个丢包 |
| TCP SACK | 2001 | 选择性确认,精确告知丢失的报文段,减少不必要重传 |
| TCP CUBIC | 2008 | Linux 默认算法,基于时间的三次函数增长,适合高带宽长延迟网络 |
| TCP BBR | 2016 | Google 提出,基于带宽和 RTT 建模,不依赖丢包信号,大幅提升利用率 |
BBR vs 传统算法:传统算法把丢包当做拥塞信号,而 BBR 直接测量网络的最大带宽(BtlBw)和最小 RTT(RTprop),从根本上避免了不必要的降速。在高丢包率的网络(如卫星、移动网络)中,BBR 的吞吐量优势尤为明显。
七、总结
TCP 通过两套互补的机制,实现了可靠且高效的数据传输:
┌─────────────────────────────────┐
│ TCP 速度控制体系 │
└──────────────┬──────────────────┘
│
┌────────────────────┴─────────────────────┐
↓ ↓
┌──────────────────┐ ┌─────────────────────┐
│ 流量控制 │ │ 拥塞控制 │
│ (端到端保护) │ │ (全局网络保护) │
│ │ │ │
│ 核心:rwnd │ │ 核心:cwnd │
│ 来源:接收方通告 │ │ 算法:慢启动 │
│ 目的:保护接收方 │ │ 拥塞避免 │
└──────────────────┘ │ 快速重传 │
│ 快速恢复 │
└─────────────────────┘
↓
实际发送窗口 = min(rwnd, cwnd)
↙ ↘
不撑爆接收方缓冲区 不堵死中间网络
一句话总结:
TCP 用流量控制 管「端点」------保证接收方能接住你发的数据;用拥塞控制管「网络」------保证中间网络能扛住所有人发的数据。两者共同限制实际发送窗口,既保证了端到端的传输效率,又维护了整个网络的稳定,这也是 TCP 成为互联网最核心传输协议的关键所在。
参考资料
- RFC 5681 - TCP Congestion Control
- RFC 3390 - Increasing TCP's Initial Window
- RFC 6582 - The NewReno Modification to TCP's Fast Recovery Algorithm
- 《计算机网络:自顶向下方法》(Kurose & Ross)
- 《TCP/IP 详解 卷1:协议》(W. Richard Stevens)
- Google BBR 论文:BBR: Congestion-Based Congestion Control
如有问题或建议,欢迎评论区交流!觉得有帮助的话,点个赞和收藏支持一下 😊