Detect remote disconnect (half-close) when using asynchronous sockets in C/C++
I have implemented a TCP/IP client using C sockets running in asynchronous mode (fcntl(sock, F_SETFL, O_ASYNC);
) in order to minimise blocking. The client will only send data to the server and never receive any data. Thus the loop looks something like this:
while (true) {
...
fd_set writefds;
FD_ZERO(&writefds);
FD_SET(socketFd, &writefds);
select(socketFd + 1, NULL, &writefds, NULL, &selectTimeout);
if (FD_ISSET(socketFd, &writefds)) {
ssize_t cBytes = send(socketFd, someBuffer, someSize, 0);
...
}
...
}
How do I detect if the server disconnects? As suggested in this question, an initially plausible solution is to call recv(socketFd, &buf, len, MSG_PEEK);
and check if that call returns 0. However, this clearly does not work because first of all, the server does not actually send any data in my case. Second, in the generalised case the server might not send data all the time and recv()
will thus return 0 anyway as it is non-blocking.
When using tcpdump to monitor the connection between the server and the client, the server sends a FIN packet when it closes the connection but the client does not acknowledge this. I have been unable to make it recognise the half-close using neither recv()
, send()
, nor select()
. In fact, the first indication that something is wrong is when the server sends a RST when the client tries to send data to the now closed connection:
IP6 ::1.62179 > ::1.2526: Flags [S], seq ..., length 0
IP6 ::1.2526 > ::1.62179: Flags [S.], seq ..., length 0
IP6 ::1.62179 > ::1.2526: Flags [.], ack 1, ..., length 0
IP6 ::1.2526 > ::1.62179: Flags [.], ack 1, ..., length 0
IP6 ::1.2526 > ::1.62179: Flags [F.], seq 1, ..., length 0
IP6 ::1.62179 > ::1.2526: Flags [.], ack 2, ..., length 0
IP6 ::1.2526 > ::1.62179: Flags [.], ack 1, ..., length 0
IP6 ::1.62179 > ::1.2526: Flags [P.], seq 1:7, ack 2, ..., length 6
IP6 ::1.2526 > ::1.62179: Flags [R], seq ..., length 0
How do I determine that the server has sent the FIN packet so that I can cleanly shutdown everything before trying to send the next data packet?