TCP流量控制与拥塞控制

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

阶段说明:

  1. 慢启动cwnd 从 1 开始指数增长,直到触及 ssthresh
  2. 拥塞避免cwnd 线性增长,每 RTT +1 MSS
  3. 快速恢复 :收到 3 个重复 ACK,cwnd 减半 +3,继续传输
  4. 超时重置 :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

阶段说明:

  1. 慢启动cwnd 从 1 开始指数增长,直到触及 ssthresh
  2. 拥塞避免cwnd 线性增长,每 RTT +1 MSS
  3. 快速恢复 :收到 3 个重复 ACK,cwnd 减半 +3,继续传输
  4. 超时重置 :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

如有问题或建议,欢迎评论区交流!觉得有帮助的话,点个赞和收藏支持一下 😊

相关推荐
yang)2 小时前
JESD 204b
运维·服务器·网络
倔强的石头1062 小时前
【Linux指南】基础IO系列(四):文件描述符 fd——Linux 文件操作的 “万能钥匙”
linux·运维·服务器
SPC的存折2 小时前
12、Ansible安全加固
linux·运维·服务器·安全·ansible
常利兵2 小时前
安卓开发避坑指南:全局异常捕获与优雅处理实战
android·服务器·php
多年小白2 小时前
AI 日报 - 2026年4月6日
网络·人工智能·科技·矩阵
星夜落月2 小时前
ONLYOFFICE Docs 自托管在线办公套件搭建指南
服务器·网络·onlyoffice
AI自动化工坊2 小时前
工程实践:AI Agent双重安全验证机制的技术实现方案
网络·人工智能·安全·ai·ai agent
luj_17682 小时前
从R语言想起的,。。。
服务器·c语言·开发语言·经验分享·算法
qq_339191142 小时前
kimi-cli 服务形式启动,kimi-cli无头模式 kimi-cli web启动,
服务器·前端·javascript