【Java EE】TCP—滑动窗口

滑动窗口

滑动窗口

什么是滑动窗口?

窗口大小 定义为:不需要等待确认应答,能够连续发送的最大数据量。

窗口的单位通常是字节(或数据包个数)。发送方在窗口范围内可以"一口气"发出多个数据段,然后等待这些数据段的 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~10001001~20002001~3000 三个报文段,接收方 B 对应生成 ACK 1001ACK 2001ACK 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滑动窗口

可靠性高,效率较高

相关推荐
XiYang-DING15 小时前
【Java EE】 TCP—异常情况处理
java·tcp/ip·java-ee
.千余15 小时前
【Linux】网络基础2---Socket编程预备
linux·网络·php
network_tester17 小时前
自动驾驶系统TSN时延测试:从理论到实践的关键解析
网络·人工智能·网络协议·tcp/ip·自动驾驶·信息与通信·p2p
雪度娃娃17 小时前
Asio异步读写——简单服务器和客户端异步通信
运维·服务器·网络·c++·php
XiYang-DING17 小时前
【Java EE】TCP(Transmission Control Protocol)
单片机·tcp/ip·java-ee
TechWayfarer17 小时前
街道级IP定位的技术边界:IP精准定位服务在本地生活场景的落地实践
大数据·网络·python·tcp/ip·生活
少年攻城狮17 小时前
阿里云系列---【申请域名并绑定到主机ip】
linux·服务器·tcp/ip·阿里云·云计算
XiYang-DING17 小时前
【Java EE】TCP—连接管理
网络·tcp/ip·java-ee