TCP 的 rwnd、cwnd、Window Scale、Zero Window、Persist Timer 和 Zero-Window Probe 有什么区别?
rwnd 是接收方通告的接收窗口,用于流量控制,避免发送方淹没接收缓冲区;cwnd 是发送方维护的拥塞窗口,用于限制向网络注入的未确认数据;实际可发送上限受两者较小值约束。Window Scale 是握手期间协商的 TCP 选项,用来把 16 位 Window 字段扩展为更大的实际接收窗口。
本文目录(30 节)
直接答案
rwnd 是接收方通告的接收窗口,用于流量控制,避免发送方淹没接收缓冲区;cwnd 是发送方维护的拥塞窗口,用于限制向网络注入的未确认数据;实际可发送上限受两者较小值约束。Window Scale 是握手期间协商的 TCP 选项,用来把 16 位 Window 字段扩展为更大的实际接收窗口。
Zero Window 表示接收方当前通告 rwnd=0,通常因为接收缓冲区已满;发送方进入 Persist Condition,并周期性发送 Zero-Window Probe,防止“窗口重新打开的 ACK 丢失”导致双方永久等待。Persist 不是重传超时,Zero-Window Probe 也不是 Keepalive 或拥塞探测。
一、核心概念快速对比
| 概念 | 由谁维护或通告 | 解决的问题 | 常见异常含义 |
|---|---|---|---|
rwnd | 接收方通告 | 接收端流量控制 | 应用读取慢、缓冲区压力 |
cwnd | 发送方本地维护 | 网络拥塞控制 | 丢包、ECN、拥塞算法调整 |
| Window Scale | 双方在 SYN 中协商 | 支持大于约 64 KiB 的接收窗口 | 未协商导致长肥网络吞吐受限 |
| Zero Window | 接收方通告窗口为 0 | 暂停新数据 | 接收缓冲区无可用空间 |
| Persist Condition | 发送方状态 | 避免窗口更新丢失造成死锁 | 连接长期占用资源 |
| Zero-Window Probe | 发送方探测段 | 促使对端报告当前窗口 | 对端继续回 0 或窗口已打开 |
分析抓包时要先区分“接收端装不下”和“网络认为不能再发”,二者都降低吞吐,但控制信号和修复方向不同。
二、rwnd 是什么
TCP 接收方在报文头 Window 字段中通告自己还能接受多少序列空间。发送方据此限制未确认数据,保证到达数据不超过接收缓冲和应用处理能力。RFC 5681 把最近通告的接收窗口称为 rwnd。
接收应用持续读取 Socket 时,缓冲区空间释放,TCP 栈可在后续 ACK 中扩大窗口。若应用线程阻塞、消费速度过慢或系统内存压力导致缓冲不足,窗口会缩小,甚至变为零。
三、cwnd 是什么
拥塞窗口 cwnd 是发送端每条 TCP 连接的状态变量,不直接写进 TCP Header。发送方根据拥塞控制算法、ACK、丢包、ECN 和往返时延等信号调整它,控制网络中的在途数据。
慢启动、拥塞避免、快速恢复等算法围绕 cwnd 运行。即使接收方通告很大的 rwnd,cwnd 较小时,发送方仍不能一次把整个接收窗口填满。
四、实际发送窗口怎样计算
RFC 5681 规定,发送方不能发送超过最高已确认序列号加上 min(cwnd, rwnd) 的数据。可以将实际可用上限简化为:
send_limit = min(receiver_window, congestion_window)
还要扣除已经在途且未确认的数据。因此抓包看到 rwnd 很大,并不能证明发送方应立即发满;还需观察 cwnd、Pacing、应用是否有数据和 Socket 层限制。
五、流量控制与拥塞控制的区别
流量控制保护接收端。它回答“接收方缓冲区还装得下多少”。拥塞控制保护网络路径。它回答“当前路径在不造成过度拥塞的情况下允许多少在途数据”。
接收应用慢会压低 rwnd,但不一定存在网络丢包;路径拥塞会压低 cwnd,但接收缓冲可能仍很空。把两者都称为“TCP 窗口小”会让排查方向失焦。
六、TCP Window 字段为何只有 16 位
基础 TCP Header 的 Window 字段为 16 位,未扩展时最多表达 65,535 字节左右的窗口。对高带宽、高 RTT 路径,带宽时延积可能远大于该值,发送方无法保持管道填满。
例如高速跨洲链路即使没有丢包,若接收窗口上限太小,也会呈现吞吐受限。Window Scale 由此扩展可表达的实际窗口,而不改变固定 TCP Header 字段宽度。
七、Window Scale 如何工作
RFC 7323 定义三字节 Window Scale Option,其中 Shift Count 表示二进制左移位数。建立连接后,普通报文中的 16 位 Window 值按协商位数左移,得到实际窗口。
actual_window = header_window << scale_shift
最大 Shift Count 按规范限制为 14,使允许的最大接收窗口约为 1 GiB。实现仍可选择只使用其中一部分。
八、Window Scale 什么时候协商
Window Scale 只在带 SYN 的握手报文中发送。双方都在各自 SYN 中提供该 Option,缩放才能在对应连接方向启用;没有协商成功,连接建立后不能临时补开。
SYN 和 SYN-ACK 自身的 Window 字段不应用缩放。抓包工具通常会根据握手 Option 显示计算后的窗口;如果缺少握手报文,工具可能无法正确推断比例。
九、两个方向的 Scale 可以不同吗
可以。每一端根据自己的最大接收缓冲选择用于“自己通告窗口”的 Scale,因此客户端和服务器的 Shift Count 可以不同。每个方向都要用对端在 SYN 中声明的比例解释。
不要从一个方向的 Scale 推断另一个方向,也不要把 Shift Count 当作当前窗口大小。它是固定倍率,实际 Header Window 仍会随缓冲区变化。
十、Window Scale 会扩大 cwnd 吗
不会。RFC 7323 明确说明缩放因子只作用于报文头通告的接收窗口,拥塞窗口由发送方以本地状态维护,不受该位移量直接缩放。
启用 Window Scale 只是移除接收窗口表达上限。若路径丢包严重、cwnd 很小或应用发送不出数据,增加接收缓冲不会自动提高吞吐。
十一、Zero Window 是怎样出现的
当接收缓冲区没有可用空间时,接收方可以在 ACK 中通告 Window 为 0。发送方随后应停止发送新的普通数据,但仍可按规则处理已有未确认数据,并进入零窗口探测流程。
常见根因是接收应用没有及时调用读取、进程卡顿、下游处理阻塞、内存限制或缓冲策略异常。它不是对端主动“断开连接”的信号。
十二、Zero Window 是否阻止所有报文
不是。窗口为零时,带数据的新段通常不在可接收窗口内,但 TCP 仍可交换 ACK,并必须按规范处理某些控制信息。连接可以在一端零窗口时继续另一个方向的数据与确认。
因此单向上传停住、下行控制报文仍正常并不矛盾。排查要按每个方向分别看序列号、ACK 和 Window。
十三、为什么需要 Persist Condition
假设接收方从零窗口恢复,并发出扩大窗口的 ACK,但该 ACK 在网络中丢失。接收方以为已经通知,发送方却仍认为窗口为零;若双方都不再发报文,连接会永久死锁。
发送方进入 Persist Condition 后周期性探测,使接收方重新返回当前 ACK 和 Window。RFC 6429 把这种零窗口探测状态称为 Persist Condition。
十四、Zero-Window Probe 做什么
RFC 9293 要求发送方支持零窗口探测。若有数据可用,发送方会定期发送至少一个八位字节的新数据,或重传相关数据,以触发接收方确认并报告当前窗口。
接收方即使窗口仍为 0,也应对 Probe 返回 ACK,带上下一期待序列号和当前窗口。窗口已打开时,返回值让发送方恢复正常发送。
十五、Persist Timer 与 RTO 的区别
首次零窗口 Probe 通常在窗口持续为零约一个重传超时周期后发送,后续间隔指数增加,但 Persist 目的不是判断数据因网络丢失而重传失败。
RTO 处理已发送数据迟迟未被确认;Persist 处理接收方明确不允许发送新数据,却又可能丢失窗口更新的死锁风险。二者都可能退避,但状态语义不同。
十六、Zero-Window Probe 与 Keepalive 的区别
Keepalive 用于长时间空闲连接中检查对端是否仍存在,通常由实现或应用配置,且不用于正常窗口同步。Zero-Window Probe 是 TCP 流量控制必需机制,发生在对端通告零窗口且仍有发送需求时。
抓包中两者都可能很小,但应结合前序 Zero Window、序列号、ACK 和计时行为判断,不能只凭包长命名。
十七、零窗口能持续多久
RFC 9293 允许接收方让通告窗口无限期保持关闭;只要对端持续响应 Probe,发送方按 TCP 协议不能仅因窗口长时间为零就自动断开。不过操作系统或应用可以基于资源管理策略终止连接。
这会带来资源耗尽风险。大量恶意或异常连接长期处于 Persist,可能占用 Socket、内存与状态表。服务端需要独立的应用超时和连接配额。
十八、Silly Window Syndrome 是什么
Silly Window Syndrome 指接收窗口反复只增加很小空间,发送方随之发送许多微小 Segment,造成 Header 和处理开销远高于有效数据。RFC 9293 描述了发送端与接收端避免算法。
接收方不必每释放一个字节就立即扩大窗口,发送方也不应把每个微小机会都变成小包。它与 Nagle Algorithm 互补,但触发机制不完全相同。
十九、窗口缩小意味着什么
正常实现应避免把已通告窗口的右边界向左收缩,因为发送方可能已经依据旧窗口发送数据。Window Scale 的量化会使通告变化更复杂,RFC 7323 要求实现对 Window Retraction 保持健壮。
抓包若显示窗口突然缩小,应先确认工具是否正确应用 Scale、是否发生乱序 ACK,以及看到的是原始字段还是计算值,再判断对端实现异常。
二十、窗口更新为何可能看起来乱序
单向数据流中,多个纯 ACK 可能拥有相同序列号,网络乱序后发送方难以仅按序列号判断哪个 Window 更新较新。TCP 使用 ACK、序列和更新规则保护发送窗口状态。
分析时不要简单按抓包到达顺序认定窗口“先大后小”。应结合两个端点抓包、ACK Number、时间和 TCP 栈统计。
二十一、Auto-Tuning 与 Window Scale 的关系
现代系统常根据带宽、RTT 和应用读取行为自动调整 Socket 接收缓冲。Window Scale 决定连接能表达的上限,Auto-Tuning 决定运行时实际愿意提供多大窗口。
应用在握手前把接收缓冲限制得过小,可能导致 Scale 选择和最大窗口受限。反过来,仅把系统最大缓冲调得很大,也可能增加内存占用而无吞吐收益。
二十二、带宽时延积如何使用
带宽时延积约等于保持路径满载所需的在途数据。例如 1 Gbit/s、100 ms RTT 的路径需要约 12.5 MB 在途数据。若 min(rwnd,cwnd) 长期只有 1 MB,单连接无法填满理论带宽。
这只是容量估算,不代表应机械设置同样缓冲。还要考虑拥塞算法、丢包、Pacing、应用模式、并发连接和内存预算。
二十三、怎样判断 rwnd 限制
发送方持续有数据,网络无明显丢包,且在途数据反复贴近对端通告窗口,可能是 rwnd 限制。抓包可观察 Window Full、Window Update 和 Zero Window,主机侧查看接收缓冲、应用读取速率和 Socket 统计。
若 cwnd 比 rwnd 更小,当前主要限制仍是拥塞控制。仅根据吞吐曲线不能可靠区分,最好取得发送端 TCP 内部状态。
二十四、怎样判断 cwnd 限制
发送方在对端 rwnd 仍充足时减少在途数据,并伴随丢包重传、ECN、拥塞事件或拥塞算法阶段变化,可能是 cwnd 限制。高 RTT 会让窗口增长和丢失恢复更慢。
抓包只能间接估算 cwnd,准确值应从发送主机的 TCP Diagnostics、内核追踪或协议栈指标读取。中间设备观察到的数据不足以还原所有本地状态。
二十五、Zero Window 的标准排错顺序
先确认是哪一端通告 0,以及哪个应用负责读取该方向数据。查看进程 CPU、线程阻塞、GC、磁盘或下游调用,再检查 Socket 接收队列和缓冲上限。随后确认 Window Update 是否发出并到达发送方。
若接收应用已经恢复而发送方仍 Persist,检查中间设备是否丢弃 ACK、NAT 状态是否异常,以及 TCP 实现是否正确响应 Probe。不要先用增大缓冲掩盖持续不读数据的应用缺陷。
二十六、常见误判
第一,把 rwnd 与 cwnd 当作同一个窗口。第二,把抓包 Window 原始值忽略 Scale 后直接比较。第三,认为 Scale 可在连接中途修改。第四,把 Zero Window 归因于网络拥塞。第五,把 Probe 当作应用心跳。第六,认为连接处于 Persist 就一定会自动超时断开。
第七,只在客户端抓包却判断服务器应用读取行为。第八,用全局最大缓冲证明单个 Socket 实际窗口足够。
常见问题
1. rwnd 很大,吞吐就一定高吗?
不一定。实际发送还受 cwnd、RTT、丢包、Pacing、应用供数和系统资源限制。
2. Window Scale 的数值就是窗口大小吗?
不是。它是二进制位移倍率,实际窗口由报文头 Window 字段按该倍率计算。
3. Zero Window 是否表示连接断开?
不是。它表示接收方暂时没有可用接收窗口,TCP 通过 Persist 和 Probe 等待窗口恢复。
4. Zero-Window Probe 会传输业务数据吗?
它可能发送至少一个八位字节的新数据或重传相关数据,主要目的是触发对端报告窗口,不应把它当成普通吞吐。
5. 增大接收缓冲能解决所有 Zero Window 吗?
不能。若应用持续不读取,任何有限缓冲最终都可能填满。应修复消费速度和阻塞链路,再合理调整缓冲。
结论
rwnd 保护接收端,cwnd 保护网络,实际发送受两者较小值约束;Window Scale 扩展接收窗口表达范围;Zero Window 表示接收缓冲暂不可用;Persist Timer 和 Zero-Window Probe 防止窗口恢复通知丢失造成死锁。排错必须区分应用背压、网络拥塞和协议探测,才能找到真正瓶颈。
参考来源
- IETF RFC 9293:Transmission Control Protocol,https:
/ / www. rfc- editor. org/ rfc/ rfc9293 - IETF RFC 7323:TCP Extensions for High Performance,https:
/ / www. rfc- editor. org/ rfc/ rfc7323 - IETF RFC 5681:TCP Congestion Control,https:
/ / www. rfc- editor. org/ rfc/ rfc5681 - IETF RFC 6429:TCP Sender Clarification for Persist Condition,https:
/ / www. rfc- editor. org/ rfc/ rfc6429 - IETF RFC 1122:Requirements for Internet Hosts — Communication Layers,https:
/ / www. rfc- editor. org/ rfc/ rfc1122 - IETF RFC 813:Window and Acknowledgement Strategy in TCP,https:
/ / www. rfc- editor. org/ rfc/ rfc813