我正在编写一个通过套接字(从客户端到服务器)发送数据的应用程序。我观察到非常奇怪的行为:当我杀死服务器时,首先发送后杀死行为就像服务器存在一样。下一个返回EPIPE。你能解释一下为什么吗?
这是来自客户端应用程序的strace:
(server not killed)
...
sendto(5, "\0\1\0\0\0\0\0\0000\311\0\0\211A\264R\0\0\0\0\232\377\4\0\0\0\0\0\0\0\0\0"..., 51516, MSG_NOSIGNAL, NULL, 0) = 51516
....
(server killed)
****sendto(5, "\0\1\0\0\1\0\0\0000\311\0\0\272A\264R\0\0\0\0c%\0\0\0\0\0\0\0\0\0\0"..., 51516, MSG_NOSIGNAL, NULL, 0) = 51516,****
(next send after server is killed)
sendto(5, "\0\1\0\0\2\0\0\0000\311\0\0\375A\264R\0\0\0\0d\307\n\0\0\0\0\0\0\0\0\0"..., 51516, MSG_NOSIGNAL, NULL, 0) = -1 EPIPE (Broken pipe)
问候 Ĵ
答案 0 :(得分:2)
TCP不保证在您尝试发送某些数据之前会检测到切断的连接。一旦尝试,您可能会从网络收到错误,或者发送操作可能会超时。由于应用程序级TCP写入仅在可用缓冲区空间不足时才会阻塞,因此通常会在下一个可报告这些错误的操作中报告错误。
虽然通常会立即检测到切断的连接,但并不总是如此,并且不能保证。确切的原因可能因我们不知道的很多因素而有所不同。 (服务器应用程序本身是否打开了TCP连接?或者它是继承它还是从另一个进程获取它?任何其他进程是否都有该套接字的副本?服务器是否通过连接调用fork而不是在子进程中关闭它?)
答案 1 :(得分:1)
在TCP中,连接的两个方向独立运行。当服务器关闭连接时,它会在服务器 - >客户端方向发送一条消息,表明它已完成传输;如果客户端执行read()
或recv()
,则会在此时获得EOF。
但是,此消息没有说明客户端 - >服务器方向。 TCP没有为服务器提供任何方式告诉客户端它不愿意再读取任何数据。因此,客户端无法在尝试发送之前知道发送将失败。
当服务器从客户端收到新消息时,如果没有服务器进程仍在尝试从连接中读取,则服务器会向客户端发送RST
消息。这是异步的 - 客户端上的sendto()
调用已经返回(实际上,它会在将数据复制到内核缓冲区之后立即返回,然后才会将任何内容发送到网络)。当客户端TCP堆栈收到RST
消息时,它会在套接字上设置error
标志,并且 next 时间客户端尝试对其获取的套接字执行任何操作EPIPE
错误。