先记住这个答案
接收方通过TCP报文头部窗口字段通告剩余缓冲区大小即rwnd,发送方把已发送未确认的字节数控制在这个值内,从而让接收方的处理速度反作用于发送速率。当rwnd降为0,发送方被禁止发送任何业务数据,同时启动持续计时器;计时器到点后发送一个携带1字节数据的窗口探测报文,接收方仍会回ACK并附带最新rwnd,若已恢复非零则继续发送,否则以更长间隔再次探测。该机制不依赖窗口更新报文的可靠传递,有效避免双方互相等死。
- 接收窗口只由接收缓冲区空闲量决定
- 零窗口时发送方不发数据只发窗口探测
- 窗口更新报文丢失靠探测兜底
滑动窗口用rwnd限速与死锁风险
接收进程读取速度决定缓冲区空余量。TCP每段头部Window字段通告rwnd,发送方将已发送未确认字节数控制在rwnd内。当缓冲区填满,接收方通告rwnd=0,发送方停止发送业务数据。若仅靠数据ACK传递窗口,则窗口更新报文可能丢失,发送方将无限等待,形成死锁。
解决方式是持续计时器。发送方开启persist timer,间隔采用指数退避,到点时发送仅带1字节数据的窗口探测报文。探测报文因有新数据必须被ACK,接收方回复ACK并携带当前rwnd;若仍为0则重置计时器,若大于0则发送方按新窗口恢复发送。这样即使窗口更新报文丢失,也能及时感知并打破死锁。
接收应用处理缓慢导致零窗口
条件:接收缓冲区64KB,发送方有持续大数据流待发,rwnd初值64KB。应用读取速率降至1KB/s,发送方连续发送40KB后缓冲区占用40KB,应用只读了5KB,于是剩余空闲为29KB,即rwnd=29KB;随后发送方再发送29KB,期间应用又读走1KB,rwnd降至1KB;发送方最后发出1KB且应用未读,缓冲区满,rwnd变为0。此时发送方停发,并启动持续计时器。
计时器1秒后发送1字节窗口探测,接收方回ACK仍为0。计时器2秒、4秒后重复,应用缓慢读走2KB后,探测ACK中rwnd=2048。发送方恢复发送,但为避免再次立刻填满堆积小报文,采用窗口阈值策略:只在rwnd达到一个全尺寸报文段(如MSS)时才发送,否则继续等待,防止糊涂窗口综合症。最终数据在约5秒内随应用处理速度分批送出。
零窗口机制的失效条件与代价
零窗口死锁的解除依赖持续计时器,这在TCP可靠传输下稳定,但若接收方不复用连接而是直接关闭,发送方会收到RST终止,而非继续探测。探测报文若因网络丢包而丢失,TCP重传机制能保证探测不会永久丢失。但探测本身会消耗带宽与接收方处理:即使rwnd长期为0,每个退避间隔仍会唤醒接收方一次。
另一种失效是接收方宣告的rwnd远小于MSS,发送方频繁启动停止会产生大量小报文,浪费40字节头部开销。业界建议用socket选项开启窗口收缩限制与延迟ACK,并在接收方通过动态调整缓冲区大小或预先提高SO_RCVBUF来减少零窗口概率。若延迟敏感,应用应监控吞吐骤降,而不能只依赖TCP被动等待。
容易答错的地方
- 把零窗口当拥塞窗口为0
- 零窗口是接收方缓冲区空余不足,
rwnd=0;拥塞窗口为0表示网络拥塞,cwnd初始较小但不会因接收方变0。两者主体和触发原因不同,实际发送窗口取二者较小值,但零窗口场景只应讨论rwnd。 - 窗口探测可以不带数据
- 探测必须携带1字节新数据,这样它作为数据报文才能被可靠确认并携带新
rwnd;若为空ACK序列,无法保证对端回应且可能被延迟确认合并,导致长时间无法获知窗口状态。
面试官还会怎么问?
持续计时器的超时值如何计算?
默认基本值通常为1秒,之后指数退避并设最大间隔(如60秒,具体依赖实现),窗口探测使用数据重传保证,超时变化只影响恢复速度,不破坏可靠性。
如果接收方进程一直不读,发送会永远阻塞吗?
在连接有效期内发送方会无限次探测,但TCP设了用户超时和保活机制,最终可能在数百秒后判定连接异常并终止。零窗口本身不是错误,它只是接收方繁忙的信号。
窗口通告值的单位是什么?
单位是字节。TCP首部的16位窗口字段以字节计数,最大65535字节;启用窗口缩放选项后可扩大到更大值,缩放因子由握手协商确定。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。