I work with a simple server that sends a heartbeat packet to the client every 30 seconds, which then confirms a heartbeat with a heartbeat response packet. When I brutally shut down the server by sending it SIGKILL, SIGSEGV, the client detects this using the select () and read () system calls quite easily. Then I started wondering what would happen when you do this just before the client writes down their response response packet, so I put a 20 second sleep in the client code and, on average, killed the server, but found that the message was on the side the client still succeeds. An attempt at a second recording immediately triggered the expected SIGPIPE signal and recorded the returned EPIPE. As far as I can tell, this is normal behavior, however, only out of curiosity did I print out the tcp status on the client side. It turned out:
- TCP_ESTABLISHED - before sending the SIGKILL server.
- TCP_CLOSE_WAIT - after SIGKILL on the server side before recording on the client side.
- TCP_CLOSE - after the first and second write attempts.
So my questions are:
- Why does the first record not call SIGPIPE and return EPIPE?
- Can I conclude that if the TCP state is TCP_CLOSE after the first record, that the connection to the server is not working or do I need to resend the data again to make sure?
A diagram of what happens when I understand this at the moment:
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
source
share