接收方处理不过来、窗口变成 0 之后,发送方怎么办?万一窗口重新打开的通知丢了会不会永远卡住?这篇把接收缓冲区塞满,抓发送方的零窗口探测包,再让接收方重新读数据看恢复要多久。
接收缓冲区 342972 字节;发送方 send() 共收下 488000 字节;FIONREAD 可读 342000 字节 接收方发出的 ACK 40 条(含 dummynet 之外无重复),其中窗口字段为 0 的 0 条;窗口的最后几个取值(实际窗口 = 字段 × 2^3,字节):[7976, 5976, 3976, 2976, 1976, 976]
接收方不读,窗口逐步缩小;小于发送方要发的包时就停发,等缓冲区吃满时通告窗口 0。
零窗口从 5.10 秒(相对抓包开始)起;之后的 25 秒里发送方又发出了 4 个数据包(探测)
探测 1:距零窗口 +5.0 秒,载荷 1 字节,序号(相对)= 342976
探测 2:距零窗口 +10.0 秒,载荷 1 字节,序号(相对)= 342976
探测 3:距零窗口 +15.0 秒,载荷 1 字节,序号(相对)= 342976
探测 4:距零窗口 +20.0 秒,载荷 1 字节,序号(相对)= 342976
相邻探测的间隔(秒):[5.0, 5.0, 5.0]
每个探测后接收方回的 ACK 的窗口字段:[0](0 = 仍然没有空间;确认号 没有增加:探测的字节没有被接收)窗口 0 时发送方起一个 persist 定时器,每 5 秒发一个 1 字节的探测包,接收方回当前窗口。这样即使窗口更新 ACK 丢了也不会永远死锁。
接收方在读数据之前,发送方一个 send() 已经阻塞了 0.5 秒;接收方读走 100000 字节后,这个 send() 在 1.3 ms 内返回(send 耗时 0.51 秒) 读数据之后第一个“窗口更新”ACK:读之后 0.21 ms,窗口字段 33176(非 0);之后第一个新数据包:读之后 0.58 ms 窗口更新 ACK 是纯 ACK,不会被重传;万一丢了,靠探测包重新问到窗口(第二节),这就是为什么探测必须存在
接收方一读数据就主动发窗口更新 ACK,发送方毫秒级恢复;只有更新丢了才要等下一次探测。