TCP 接收窗口 (rcv_wnd) 坍缩的根本原理,是接收端应用层的读取速度(消费)远低于网络数据的到达速度(生产),或者内核底层的缓冲区资源被无效数据耗尽。

在 TCP 协议栈中,具体成因分为以下四个核心机制:

1. 应用层消费迟缓(最常见原因)
TCP 接收到的数据会先暂存在内核的 Socket 接收缓冲区中,等待应用层(如 NVMe 驱动程序或存储应用)通过 read()/recv() 调用取走。如果应用层由于 CPU 瓶颈、磁盘 I/O 阻塞、或者使用了低效的同步 I/O 模型,导致读取速度跟不上网络的接收速度,内核缓冲区就会逐渐堆满。剩余可用空间(即 rcv_wnd)被迫不断缩小,最终引发窗口坍缩甚至零窗口(Zero Window)。

2. 丢包导致的乱序队列 (OFO Queue) 阻塞
在长距离广域网 (WAN) 环境下,网络丢包率通常较高。TCP 必须保证数据的按序交付。如果中间某一个包丢失,后续正常到达的包将无法交给应用层,内核只能将它们暂存在乱序队列(Out-of-Order Queue)中。
在丢失的包重传到达之前,后续到达的报文会迅速塞满整个 Socket 缓冲区。此时应用层处于“饥饿”状态(无序可读),而内核缓冲区已被占满,导致 rcv_wnd 断崖式坍缩。

3. BDP 与 Socket 缓冲区配置严重错配
在高带宽延迟乘积 (BDP) 的网络中,网络链路上“飞行中 (In-flight)”的数据量极大。如果 Linux 系统的 net.ipv4.tcp_rmem 或 rmem_max 设置过小,哪怕应用层处理速度很快,一个瞬间的网络突发流量 (Burst) 就能直接填满微小的接收缓冲区,迫使 TCP 频繁通告小窗口,表现为剧烈的窗口坍缩。

4. 系统级 TCP 内存压力 (TCP Memory Pressure)
当服务器上并发连接极多,全局 TCP 缓冲区内存占用超过了 net.ipv4.tcp_mem 配置的阈值时,Linux 内核为了系统自保,会进入 TCP 内存受限模式。在此状态下,内核会强行收缩所有 TCP Socket 的接收窗口(无视应用层是否真的来不及读),以强制降低对端发送速度并回收内存。

底层机制延伸(为何表现为 MSS 极度变小):
当窗口坍缩到极限时,会触发糊涂窗口综合征 (Silly Window Syndrome, SWS) 的边缘条件。内核为了防止对端发送有效载荷只有几字节的微小报文(这会造成极大的网络头部开销),可能会在协议栈内部调整 MSS 预估值,或者对端发送算法(Nagle 的变体)为了适应极其狭窄的窗口,退化为发送极小的数据块,这在诊断工具层面就表现出了类似 MSS 被错误钳制到 259 这种奇异值的症状。