据我所知,一旦建立了 TCP 连接,除非两端中的一端实际发送了消息,否则就不会有实际的数据流,它们都会被阻塞在接收消息的调用中。也就是说,两端都不会定期向另一端发送消息说“只是为了让你知道,我还活着”。
如果是这样的话,那么我预计如果通信通道的任一端突然关闭(例如kill -KILL it
,但也只是拔掉整台机器的电源线),那么另一端就不会知道。
举一个具体的例子,我可以通过以下方式在两个终端之间建立连接socat
:
- 从一个终端监听并记下 PID
$ socat -d4 TCP-LISTEN:12345 - $ # ctrl-z $ jobs -l [1]+ 958602 Stopped socat -d4 TCP-LISTEN:12345 - $ fg
- 从另一个终端连接并记下该 PID
$ socat -d4 TCP-CONNECT:localhost:12345 - $ # ctrl-z $ jobs -l [1]+ 958730 Stopped socat -d4 TCP-CONNECT:localhost:12345 - $ fg
(由于调试消息,两个socat
命令都会产生相当多的输出-d4
。)
现在,如果我打开第三个终端问题
$ kill -KILL 958730
我看到在第二个Killed
终端中打印的内容,其中的调试行并不比之前打印的多,但在第一个终端中我确实看到了更多的输出:socat
2025/02/10 18:04:42 socat[958602] D select -> (, 0x0, 0x0, 0x0, NULL/0.000000), 1
2025/02/10 18:04:42 socat[958602] D read(122, 0x61dbb1b48000, 8192)
2025/02/10 18:04:42 socat[958602] D read -> 0
2025/02/10 18:04:42 socat[958602] N socket 1 (fd 122) is at EOF
2025/02/10 18:04:42 socat[958602] D data loop: sock1->eof=3, sock2->eof=0, closing=1, wasaction=1, total_to={0.1000000}
2025/02/10 18:04:42 socat[958602] D select(1, &0x1, &0x0, &0x0, &0.500000)
2025/02/10 18:04:43 socat[958602] D select -> (, 0x0, 0x0, 0x0, &0.000000), 0
2025/02/10 18:04:43 socat[958602] I poll timed out (no data within 0.500000 seconds)
2025/02/10 18:04:43 socat[958602] I shutdown(122, 2)
2025/02/10 18:04:43 socat[958602] D shutdown() -> 0
2025/02/10 18:04:43 socat[958602] D tcsetattr(0, 0, {00006506,00000005,000000bf,00008a3b, 15,15, 03,1c,7f,15,04,00,01,00,11,13,1a,00,12,0f,17,16,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00})
2025/02/10 18:04:43 socat[958602] D tcsetattr() -> 0
2025/02/10 18:04:43 socat[958602] D tcsetattr(1, 0, {00006506,00000005,000000bf,00008a3b, 15,15, 03,1c,7f,15,04,00,01,00,11,13,1a,00,12,0f,17,16,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00})
2025/02/10 18:04:43 socat[958602] D tcsetattr() -> 0
2025/02/10 18:04:43 socat[958602] N exiting with status 0
2025/02/10 18:04:43 socat[958602] D exit(0)
2025/02/10 18:04:43 socat[958602] D starting xioexit()
2025/02/10 18:04:43 socat[958602] D finished xioexit()
但我不明白,如果因为SIGKILL
它是由内核直接处理的而不能在用户代码中捕获/处理,那么958730
就不应该做任何事情,比如发送一条消息说“对不起,我快死了”,因此958602
永远不应该知道这件事。
我的推理缺陷在哪里?