“对端挂了,我什么时候才知道?”取决于怎么挂的:进程崩溃、主机断电、网线被拔,线路上留下的痕迹完全不同。这篇用 kill -9、SIGSTOP、SO_LINGER 0 和丢包注入逐种实测,看哪些情形本端立刻知道,哪些会形成半开连接。
先正常通信:收到 b'pong'
SIGKILL 之后线上的包:
+ 0.000 s 服务端→客户端 FIN+ACK 载荷 0 字节
+ 0.000 s 客户端→服务端 ACK 载荷 0 字节
客户端 recv 返回 b''(空 = 对端有序关闭 FIN)对端进程被 kill -9,内核发 FIN,本端立刻读到 EOF。真正悄无声息的是整台机器或连接状态消失。
对端冻住后客户端一直写,直到发送缓冲区和对端接收缓冲区都满,共写进 1400312 字节(1.34 MiB)后 send 返回 EWOULDBLOCK;线上服务端(内核)一直在回 ACK,共 33 个纯 ACK
SIGSTOP 的进程内核仍然 ACK,线路上看不出异常,只是窗口最终会满。
客户端 send 报错:[Errno 32] Broken pipe
第二次 send 报错:EPIPE Broken pipe
线上的包:
+ 0.000 s 服务端→客户端 RST+ACK 载荷 0 字节对端用 SO_LINGER(1,0) 关闭会发 RST,本端下一次 send 就是 EPIPE。
40 个连接里:客户端已被通知(ECONNRESET 或 EOF)的 22 个;毫无察觉的半开 18 个(RST 在 netlab 里被丢了)
对这些半开连接各发一次数据后(服务端没有这个连接,会回 RST;RST 同样有 50% 被丢):{'ECONNRESET': 14, '仍无察觉': 4}RST 在路上丢了,本端还是 ESTABLISHED。只有本端发数据去问,对端才会回 RST;这个 RST 也可能再丢。
connect 到 example.com:81 在 75.0 秒后失败:ETIMEDOUT Operation timed out 系统参数:net.inet.tcp.keepinit: 75000;net.inet.tcp.msl: 15000
connect 到黑洞 75.0 秒才超时,等于 keepinit。已建立连接遇黑洞要等多久,本篇没有测。