TCP 零窗口是什么?为什么需要零窗口探测?
简化版
TCP 零窗口表示接收方通告 rwnd=0,告诉发送方接收缓冲区满了,暂时不要再发数据。为了避免窗口恢复通知丢失导致双方永久等待,发送方会启动 persist timer,周期性发送零窗口探测包。
详细版
零窗口是 TCP 流量控制的边界场景:
- 接收方应用读取太慢,内核接收缓冲区被占满,就会通告窗口为 0。
- 发送方看到
rwnd=0后停止发送普通数据,避免压垮接收方。 - 如果接收方后来有空间,会通告新的窗口。
- 这个窗口更新报文也可能丢失,所以发送方不能无限等待。
- 发送方会用零窗口探测确认窗口是否恢复,避免死锁。
面试要强调:零窗口不是拥塞控制,而是接收方流量控制;零窗口探测解决的是“窗口更新丢失导致双方互等”的问题。
完整版教学
一、零窗口从哪里来
TCP 接收方有接收缓冲区。网络包到达后,内核先放入缓冲区,应用再通过 read 消费。如果应用读取速度慢于网络到达速度,缓冲区可用空间会越来越小,最终可能变成 0。
当可用空间为 0 时,接收方会在 ACK 中通告 rwnd=0。这不是说连接坏了,而是在做流量控制:告诉发送方“我现在没有地方放新数据”。
接收缓冲区 8KB:
已占用 8KB,可用 0KB
=> 通告窗口 rwnd = 0
记忆钩子:零窗口是接收方喊“先别发,我缓存满了”,不是网络说“我堵了”。
二、发送方收到零窗口后做什么
发送方收到 rwnd=0 后,不能继续按原速发送普通数据,否则接收方没有空间接收,只会造成无意义重传和缓存压力。它会暂停发送新数据,并等待接收方通告窗口恢复。
但已经发送未确认的数据仍然要按 TCP 可靠传输机制处理。零窗口只限制“新数据发送”,不代表连接状态进入关闭。
| 项目 | 零窗口时的行为 |
|---|---|
| 新应用数据 | 暂停发送 |
| 已发送未确认数据 | 继续等待 ACK 或重传 |
| ACK 报文 | 仍可收发 |
| 连接状态 | 通常仍是 ESTABLISHED |
这个点很容易和连接断开混淆。零窗口可能让应用写入阻塞或发送缓冲区堆积,但它本身不是 FIN/RST。
三、为什么需要零窗口探测
设想一个场景:接收方缓冲区满了,通告 rwnd=0;过一会儿应用读走数据,接收方发送窗口更新 rwnd=4096;但这个窗口更新 ACK 在网络中丢了。此时接收方以为自己已经通知了,发送方以为窗口仍是 0,双方就可能一直等。
零窗口探测就是为了解这个死锁。发送方启动 persist timer,到时间后发送一个很小的探测段,迫使接收方回复当前窗口大小。
Receiver -> Sender: rwnd=0
Receiver 应用读取,窗口恢复到 4096
Receiver -> Sender: rwnd=4096 (丢失)
Sender 等待中...
Sender -> Receiver: zero window probe
Receiver -> Sender: ACK, rwnd=4096
四、零窗口和拥塞窗口有什么区别
零窗口来自接收方的 rwnd,属于流量控制;拥塞窗口 cwnd 来自发送方对网络拥塞程度的判断,属于拥塞控制。实际可发送数据受两者共同限制,但它们保护的对象不同。
可发送窗口 = min(rwnd, cwnd)
rwnd = 0, cwnd = 64KB => 仍然不能发新数据
rwnd = 64KB, cwnd = 2KB => 只能谨慎发 2KB
如果线上看到零窗口,重点查接收端应用读取、CPU、GC、磁盘、下游阻塞,而不是先调拥塞控制参数。
五、零窗口会造成哪些线上表现
零窗口会让发送方数据堆在发送缓冲区,应用写入可能变慢或阻塞。对于 HTTP、数据库连接、消息系统等长连接协议,零窗口常常意味着对端应用处理不过来,或者某个线程卡住没有消费 socket。
| 现象 | 可能含义 | 排查方向 |
|---|---|---|
抓包出现 Win=0 | 接收端缓冲区满 | 看接收端应用是否读取 |
| 发送端写阻塞 | 对端不再接收新数据 | 看 socket buffer 和业务线程 |
| 周期性小包探测 | persist timer 工作 | 判断窗口是否恢复 |
| RTT 正常但吞吐为 0 | 非网络拥塞 | 查应用消费速度 |
举例:服务端生成响应很快,但客户端因为本地处理慢没有及时读取,客户端通告零窗口。服务端此时不是“网络发不出去”,而是被客户端流量控制按住了。
六、Silly Window Syndrome 和小窗口问题
零窗口恢复后,如果接收方每腾出几个字节就通告很小窗口,发送方每次也只发很小段,会造成大量小包,降低效率。这类问题叫 Silly Window Syndrome。
解决思路通常是避免过小窗口频繁通告,或者发送方等到有足够数据/窗口再发送。它和 Nagle、延迟 ACK 等机制有一定关联,但核心仍然是减少小包低效传输。
低效恢复:
rwnd=1 -> 发1字节
rwnd=2 -> 发2字节
rwnd=3 -> 发3字节
更合理:
等待窗口恢复到 MSS 或缓冲区较大比例后再发送
面试不必展开所有算法细节,但提到“小窗口会造成小包放大和吞吐下降”,会显得理解更完整。
七、常见误区与追问
- 误区:零窗口说明网络拥塞。 零窗口来自接收端缓存不足,是流量控制,不是拥塞控制。
- 误区:rwnd=0 后连接就断了。 连接仍可保持
ESTABLISHED,只是暂停发送新数据。 - 误区:等待窗口恢复通知就够了。 窗口更新报文可能丢失,所以需要零窗口探测避免死锁。
- 追问:零窗口探测发什么? 通常发送很小的探测段,让接收方回复当前窗口大小。
- 追问:看到大量零窗口先查哪里? 优先查接收端应用是否及时 read,以及 CPU、GC、磁盘、下游依赖是否卡住。
- 追问:零窗口和发送缓冲区满有什么关系? 对端零窗口会让本端新数据发不出去,本端发送缓冲区可能逐渐堆满。
八、加强记忆
零窗口按“满、停、探、复”记:接收缓冲区满,通告 rwnd=0;发送方停止新数据;persist timer 触发探测;窗口恢复后继续发送。答题时一定点出它是流量控制边界,不是拥塞控制或连接关闭。