2014-07-10 24 views
4

我一直在使用一个简单的服务器,每30秒向客户端发送一个心跳数据包,然后用心跳回复数据包确认心跳。当我通过发送它SIGKILL,SIGSEGV来粗暴地终止服务器时,客户端通过select()和read()系统调用足以发现这一点。然后,我开始想知道在客户端写入其心跳回复数据包之前这样做时发生了什么,所以我在客户端代码中放置了20秒的睡眠时间,同时杀死了服务器,但发现客户端写入仍然成功。之后立即尝试第二次写入,触发了预期的SIGPIPE信号并写入返回的EPIPE。据我所知这是正常的行为,但是,出于好奇,我打印出客户端的TCP状态。原来是:套接字,TCP状态和写系统调用

  1. TCP_ESTABLISHED - 在发送服务器SIGKILL之前。
  2. TCP_CLOSE_WAIT - 在第一次客户端写入之前的服务器端SIGKILL之后。
  3. TCP_CLOSE - 第一次和第二次写入尝试后。

所以我的问题是:

  1. 为什么第一次写入没有提出SIGPIPE并返回EPIPE?
  2. 我可以得出结论:如果在第一次写入连接到服务器的连接关闭后,TCP状态是TCP_CLOSE,还是必须再次发送一次数据才能确定?

,我此刻明白发生了什么的示意图:

     server        client 

      [ESTABLISHED] |          | [ESTABLISHED] 
SIGKILL or close() --> |          | 
      [FIN_WAIT_1] |------------FIN M------------------->| [CLOSE_WAIT] 
         |          |   ---\ 
      [FIN_WAIT_2] |<-----------ACK M+1------------------|    | 
         |          |    | a read performed after a 
      [TIME_WAIT] |<-----------FIN N--------------------| [LAST_ACK?] |-- serverside SIGKILL returns 0 
         |          |    | but write succeeds 
         |------------ACK N+1----------------->| [CLOSE]  | 
         |          |   ---/ 
         |          | 
         |          |   ---\ 
         |          | [CLOSE]  | After the first write returns 
         |          |    | the TCP/IP state is CLOSED 
         |          | [CLOSE]  | but even so only the a second 
         |          |    | returns EPIPE and raises SIGPIPE. 
         |          | [CLOSE]  | 
         |          |    v 
+1

[写入封闭的本地TCP套接字不失败](http:// stackoverflow。com/questions/11436013 /写封闭本地-tcp-socket-not-failing) – jxh

回答

3

为什么第一次写入没有提出SIGPIPE并返回EPIPE?

TCP是异步的。您的写入只将数据复制到套接字缓冲区并返回。 TCP堆栈在后台接管并致力于发送该数据。换句话说,send/sendmsg/write返回时并不意味着数据已发送。

当服务器被杀害,内核确实close插座为大家讲解,选派优秀的数据,然后FIN,这使你的客户端套接字到TCP_CLOSE_WAIT状态。这是一个半开放的连接状态,客户端仍然可以发送数据,只要服务器期望它。

您的客户端发送更多数据,但服务器操作系统响应RST,因为没有处理传入数据的进程。这将您的客户端套接字放入TCP_CLOSE

我可以得出结论:如果在第一次写入服务器连接关闭后,TCP状态是TCP_CLOSE,还是必须再次发送一次数据才能确认?

TCP_CLOSE是最终的TCP状态。不确定你在问什么,但是如果你需要确保另一方接收和处理你的数据,你需要发回一些应用级别的消息。

+0

关于你最后的报价。如果我的理解正确,在第一次写入之后,我无法确定服务器是否关闭,直到我发送应用程序级别的数据,例如重复我的心跳回复数据包以确保服务器真的关闭?问题是,到第一次写入完成时,客户端已经知道套接字处于关闭状态,因为它处于TCP_CLOSE状态,它让我困惑不解,它不仅仅是在第一次写入之后立即通过引发SIGPIPE来进行报告。当我发送更多数据时,客户端不会再次回到TCP_ESTABLISHED。 –

+1

_第一次写入后,我无法确定服务器是否关闭,直到我发送应用程序级别的数据,例如重复我的心跳回复数据包以确保服务器真的关闭?_ - 当您收到FIN时,是否含糊不清您仍然可以发送数据,因为连接可以是半开或关闭。第一次写入没有立即收到服务器的响应,RST稍后到达,在第二次写入时发现。 –

+0

因此,基本上即使客户端在第一次写入后处于TCP_CLOSE状态,但不能保证服务器不可访问,并且直到RST到达第二次写入后才能知道。因此,在写入时检查断开连接的正确方法是(1)对TCP_CLOSED进行写入,(2)测试,(3)如果状态为TCP_CLOSED,再次写入数据以确保RST已经到达,并且连接是真正关闭? –