滑动窗口
- 滑动窗口
-
- 什么是滑动窗口?
- [发送策略:等所有 ACK 回来还是收到一个就发?](#发送策略:等所有 ACK 回来还是收到一个就发?)
- 累计确认:ACK丢失问题⭐
- 乱序ACK的处理⭐
- 快速重传:数据丢失问题⭐
-
- [为什么重传成功后 ACK 直接变成 7001?](#为什么重传成功后 ACK 直接变成 7001?)
- 超时重传和快速重传机制的分工⭐
- 窗口动态调整
滑动窗口
什么是滑动窗口?
窗口大小 定义为:不需要等待确认应答,能够连续发送的最大数据量。
窗口的单位通常是字节(或数据包个数)。发送方在窗口范围内可以"一口气"发出多个数据段,然后等待这些数据段的 ACK。
发送缓冲区
已发送已确认
已发送未确认
可发送未发送
不可发送
上图中,黄色部分为"已发送但未收到 ACK"的数据,蓝色部分为"窗口内待发送的数据"。随着 ACK 的到达,窗口会向右滑动,新的数据进入窗口并被发送。
发送策略:等所有 ACK 回来还是收到一个就发?
初学者常会疑惑:发送方应该等窗口内所有 ACK 都收到后再发下一批,还是每收到一个 ACK 就立即发一个新的?
答案是后者 。TCP 采用的是流水线 和滑动窗口 机制:发送方可以连续发出窗口允许的多个数据段,而不必等前一个确认返回。每收到一个累计 ACK,窗口便向右滑动,腾出的空间立即允许发送新的数据段。
下面以窗口大小为 3 个段(每个段 1000 字节)为例,演示这一过程:
主机B(接收方) 主机A(发送方) 主机B(接收方) 主机A(发送方) 发送窗口 [1~3000] 窗口用尽,等待确认 收到 1~3000 窗口右滑 → [3001~6000] 立即发送 3001~4000 收到 1~4000 窗口再次右滑 → [4001~7000] 立即发送 4001~5000 数据包 1~1000 数据包 1001~2000 数据包 2001~3000 ACK 3001(确认收到 1~3000) 数据包 3001~4000 ACK 4001(确认收到 1~4000) 数据包 4001~5000
图解说明:
- 主机 A 一开始便连发 3 个数据段,把窗口填满。
- 收到第一个 ACK(累计确认 1~3000)后,窗口右移 至
3001~6000,A 立即将3001~4000发送出去。 - 收到第二个 ACK(累计确认 1~4000)时,窗口继续右移,又可发送下一个段。
整个过程无需等待所有 ACK 返回,一个 ACK 就能推动窗口滑动并触发一次新发送,传输链路始终保持忙碌,大大减少了空闲等待时间,这就是滑动窗口流水线的高效所在。
累计确认:ACK丢失问题⭐
网络传输中,ACK 本身也可能丢失。假设主机B收到了数据包1~5000,但返回给主机A的某些ACK在途中丢失了。此时需要重传吗?
不需要 。TCP 使用累计确认机制:ACK 携带的是"下一个期望收到的字节序号"。例如:
- 收到1~1000后,回复 ACK: 下一个是1001
- 又收到1001~2000,回复 ACK: 下一个是2001
- 如果 ACK(2001) 丢失,但随后收到3001~4000,则接收方会回复 ACK: 下一个是4001
这个 ACK(4001) 隐含地确认了4001之前的所有数据(包括1~3000)。因此,即使中间的ACK丢失,只要后续有更高序号的ACK到达,发送方就知道之前的数据已被成功接收。
主机B 主机A 主机B 主机A ACK 2001 在传输中丢失 主机A收到ACK 4001 就知道1~4000已全部确认 即使ACK 2001丢了也无需重传 发送数据 1~1000 ACK 1001 (确认收到1~1000) 发送数据 1001~2000 ACK 2001 ❌ 丢失 发送数据 2001~3000 ACK 3001 (确认收到1~3000) 发送数据 3001~4000 ACK 4001 ✅ 到达 (确认收到1~4000)
上图中,ACK(2001)丢失,但主机A随后收到了ACK(4001)。根据累计确认,ACK(4001)代表1~4000全部确认,因此无需重传任何数据。这就是"后一个ACK能涵盖前一个ACK的含义"。
乱序ACK的处理⭐
由于网络延迟不同,ACK 确实可能不按顺序到达。例如,主机 A 连续发送了 1~1000、1001~2000、2001~3000 三个报文段,接收方 B 对应生成 ACK 1001、ACK 2001、ACK 3001。
若 ACK 3001 先到达,ACK 2001 后到达,会发生什么?
滑动窗口天然支持乱序 ACK,处理规则极其简单:永远以收到的最大确认序号为准。
ACK 3001是一个累计确认,表示"1~3000已全部收到"。- 主机 A 收到后,立即将发送窗口左边界滑动到
3001,此时即使2001~3000刚刚才被确认,也不影响窗口前进,3001~4000可以马上发送。 - 随后,迟到的
ACK 2001到达。由于它要确认的范围1~2000早已被ACK 3001覆盖,这条 ACK 就是冗余信息,直接忽略。窗口不会倒退,也不会做任何重传。
整个过程如下图所示:
主机B 主机A 主机B 主机A 生成 ACK 1001、2001、3001 窗口右移至 3001 可发送 3001~4000 确认范围已被覆盖,忽略 数据 1~1000 数据 1001~2000 数据 2001~3000 ACK 3001(先到,确认1~3000) ACK 2001(后到,冗余) 数据 3001~4000
核心要点:
- 累计确认让 TCP 只关心"已连续确认到哪个序号",不依赖 ACK 的到达顺序。
- 窗口只沿序号增大方向滑动,不后退、不重传已确认数据。
- 迟到的、确认范围更小的 ACK,在更大的 ACK 面前直接变成冗余,安全丢弃。
快速重传:数据丢失问题⭐
当发送方一口气发出整个滑动窗口的数据时,某个中间数据段一旦丢失,接收方会收到大量跳过空洞的后续数据。此时 TCP 的累积确认 规则会让接收方不断发送相同的 ACK (期望缺失的那个起始序号),这就是重复 ACK。
快速重传的规则是:
发送方只要连续收到 3 个重复 ACK (即一共 4 个相同的 ACK),就立刻判定对应的数据段已丢失,不等待超时,马上重传该段。
假设每个数据段固定 1000 字节 ,起始序号为 1。发送方一口气发出了 1~7000 的数据(共 7 个段),但第二个段 1001‑2000 在网络中丢失了。
1. 接收方不断催促"我要 1001"
- 主机 A 发送:1‑1000、1001‑2000(丢失)、2001‑3000、3001‑4000、4001‑5000、5001‑6000、6001‑7000。
- 主机 B 收到 1‑1000,回复
ACK=1001(期望下一字节是 1001)。 - 随后 B 陆续收到 2001‑3000、3001‑4000...... 但由于 1001‑2000 缺口存在,这些段无法累积确认,B 只能一次又一次 地回复
ACK=1001。 - 于是主机 A 会接连收到多个完全相同的 ACK。
2. 发送方果断重传
当主机 A 连续收到 3 个重复的 ACK=1001 时,它立即意识到 1001‑2000 已丢失,不等计时器超时,直接重传该段。
3. 缺失补齐,ACK 一次跳跃
主机 B 终于收到重传的 1001‑2000,它检查缓冲区发现:
- 缺口之前:1‑1000 已确认。
- 缺口本身:1001‑2000 刚到位。
- 缺口之后:2001~7000 早已在缓冲区中排队。
现在从 1 到 7000 的所有字节都已连续收到,于是 B 回应一个 ACK=7001,表示"下一个期望的是第 7001 字节"。原本一直在喊"1001",补上缺失的一块后,确认号一下子就跳到了 7001。
接收方(主机B) 发送方(主机A) 接收方(主机B) 发送方(主机A) 滑动窗口批量发送,段1001-2000丢失 累计收到3个重复ACK=1001 触发快速重传 收到缺失段,缓冲区已有1~7000连续数据 继续正常传输7001之后的数据 Seq=1, Len=1000 (1~1000) Seq=1001, Len=1000 (丢失) Seq=2001, Len=1000 Seq=3001, Len=1000 Seq=4001, Len=1000 Seq=5001, Len=1000 Seq=6001, Len=1000 ACK=1001 (期望1001) ACK=1001 (重复,因收到2001-3000) ACK=1001 (重复,因收到3001-4000) ACK=1001 (重复,因收到4001-5000) 重传 Seq=1001, Len=1000 ACK=7001 (累积确认向前跳跃)
为什么重传成功后 ACK 直接变成 7001?
这正是 TCP 累积确认的威力。
- 在 1001‑2000 补上之前,接收方虽然已经正确收到了 2001~7000,但因为数据流中间有个空洞,协议不允许确认不连续的部分,所以 ACK 只能停留在 1001。
- 一旦缺失的段被填充,接收方就能把 1 到 7000 的所有数据一次性"拼图成功",期望序号立刻跳到 7001。
这种设计既保证了可靠性,又能通过一次 ACK 确认一大批数据,非常高效。
超时重传和快速重传机制的分工⭐
快速重传并非要取代超时重传,它们有着明确的分工。下面的流程图展示了发送方的完整决策逻辑:
是
否/超时
是
否
是
否
发送数据段
收到 ACK?
正常更新窗口
重传计时器超时?
超时重传
收到 3 个重复 ACK?
快速重传丢失段
继续等待 ACK 或更多重复 ACK
再将两者特点浓缩为一表:
| 机制 | 核心思想 | 依赖条件 | 效率 |
|---|---|---|---|
| 超时重传 | 等待定时器到期后重传 | 任何场景均可兜底 | 较低,但绝对可靠 |
| 快速重传 | 通过重复 ACK 提前触发重传 | 需要滑动窗口批量发送,产生 ≥3 个重复 ACK | 高,能快速恢复 |
- 两者相辅相成:实际通信中,优先由快速重传修复大部分丢包(当前网络几乎总是批量传输),极少数无法产生重复 ACK 的场景(例如窗口末尾丢包)再由超时重传来兜底。
- 深刻理解这两大重传机制,是掌握 TCP 拥塞控制(慢启动、快重传/快恢复)必不可少的基础。
窗口动态调整
窗口越大,一次批量发送的数据越多,网络利用率越高。但窗口不能无限大,原因有二:
- 可靠性风险:窗口过大,一旦发生丢包,需要重传的数据量也大,可能加剧网络拥堵。
- 接收方缓存限制:接收方通告的"接收窗口"是发送窗口的上限,不能超出。
因此,实际发送窗口 = min( 接收方窗口 , 拥塞窗口 ) 。
其中拥塞窗口由发送方根据网络状况动态调整:收到新的 ACK 时逐步增大(慢启动、拥塞避免),检测到丢包时迅速减小(快重传、快恢复)。
滑动窗口无法消除可靠性带来的开销 (重传、排序、连接管理等),因此 TCP 的效率不可能超过无连接的 UDP。UDP 只管发送,不关心是否到达,速度最快;TCP 的滑动窗口只是让可靠传输的折损变得更小。
效率对比
引入滑动窗口后
UDP
无可靠性,效率最高
TCP停-等
可靠性高,效率极低
TCP滑动窗口
可靠性高,效率较高