RST 到底在什么时候出现?它的序号是怎么填的,应用侧看到的是 Connection refused、reset by peer 还是 Broken pipe?这篇抓真实的 RST 包验证填法,并把四种常见错误码对应到具体的场景。
connect 一个没人监听的端口:ConnectionRefusedError[Errno 61] Connection refused SYN:seq=2483746472;回来的包:标志 ACK+RST,seq=0,ack=2483746473 = SYN 的 seq + 1:True——规则:对一个没有连接的 SYN,RST 的序号取 0、确认号取 SYN 序号 + 1(因为 SYN 占一个序号)
连接没人监听的端口:ACK+RST,序号 0,确认号 = SYN 序号 + 1,应用得到 ECONNREFUSED。
应用依次看到:send #1 成功;send #2 BrokenPipeError[Errno 32] Broken pipe;send #3 BrokenPipeError[Errno 32] Broken pipe;recv ConnectionResetError[Errno 54] Connection reset by peer 线上:客户端:ACK seq=1 ack=1 → 服务端:ACK seq=1 ack=1 → 服务端:ACK+FIN seq=1 ack=1 → 客户端:ACK seq=1 ack=2 → 客户端:ACK+PSH seq=1 ack=2 载荷100 → 服务端:RST seq=2 ack=0 RST 的序号 3977457150 = 触发它的数据包的确认号 3977457150:True——规则:对一个带 ACK 的段,RST 的序号取该段的确认号,这样发送方才会接受它(落在期望序号上)
连接已不存在时再发数据,对端回 RST,序号取该段的确认号;应用先成功一次 send,之后 EPIPE,recv 得 ECONNRESET。
服务端发了 hello 再中止;客户端(RST 到达之后)依次:recv #1 → b'hello';recv #2 ConnectionResetError[Errno 54] Connection reset by peer 线上:客户端:ACK seq=1 ack=1 → 服务端:ACK seq=1 ack=1 → 服务端:ACK+PSH seq=1 ack=1 载荷5 → 客户端:ACK seq=1 ack=6 → 服务端:ACK+RST seq=6 ack=1 对照:服务端发了 hello 再正常 close(FIN);客户端依次:recv #1 → b'hello';recv #2 → b''
RST 之前已经到达的数据在这台机器上仍可读,RST 之后才是错误;而 FIN 之后是正常的 EOF。
连接没人监听的端口 connect → ConnectionRefusedError[Errno 61] Connection refused
对端 RST 中止后 recv → ConnectionResetError[Errno 54] Connection reset by peer
对端 RST 中止后 send → BrokenPipeError[Errno 32] Broken pipe
对端 close 后我方第二次 send send → BrokenPipeError[Errno 32] Broken pipe
SYN 无回应(设了 2 秒超时) connect → TimeoutError[Errno None] None
ECONNREFUSED = 对 SYN 回了 RST;ECONNRESET = 连接已建立后收到 RST;EPIPE = 向已经收到 RST / FIN 的连接再写(默认还会给进程发 SIGPIPE,Python 屏蔽了它);ETIMEDOUT / 超时 = 对方没有任何回应ECONNREFUSED、ECONNRESET、EPIPE 分别对应对 SYN 的 RST、连接中的 RST、向已关闭的连接写;超时则是对方毫无回应。