C++ How to Use Select to See If a Socket Has Closed

c++ how to use select to see if a socket has closed

The below snippet first checks if the socket is marked readable (which it is when closed) and then if there's actually anything to be read.

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>

bool isclosed(int sock) {
fd_set rfd;
FD_ZERO(&rfd);
FD_SET(sock, &rfd);
timeval tv = { 0 };
select(sock+1, &rfd, 0, 0, &tv);
if (!FD_ISSET(sock, &rfd))
return false;
int n = 0;
ioctl(sock, FIONREAD, &n);
return n == 0;
}

using select() to detect connection close

select() modifies readset to remove socket(s) that are not readable. Every time you call select(), you have to reset and fill readset with your latest list of active sockets that you want to test, eg:

fd_set readset;
int max;

while (1)
{
FD_ZERO(&readset);
max = -1;

// populate readset from list of active sockets...
// set max according...

printf("Waiting on select()...\n");
result = select(max + 1, &readset, NULL, NULL, NULL);
if (result < 0)
{
printf("select() failed");
break;
}

if (result == 0)
continue;

for (int i = 0; i <= max; ++i)
{
if (FD_ISSET(i, &readset))
{
result = recv(i, buffer, sizeof(buffer), 0);
if (result <= 0)
{
close(i);
// remove i from list of active sockets...
}
}
}
}

Detecting whenever socket disconnected using select()

When a socket closes it becomes "readable" but calling recv will return 0 bytes. Using select you can tell when the socket can be read then when reading it if recv returns 0 then you know it has closed.

Your comment "bytes available" isn't exactly accurate. The socket may be read from however, if it is closed there will be no bytes available.

else if(result > 0 && FD_ISSET(this->sockfd, &fd))
{
return 1; // bytes available.
}

In non-blocking sockets, recv will return -1 and set errno to EWOULDBLOCK (or EAGAIN) if there is no data and the socket is not closed.

Is there a way to detect that TCP socket has been closed by the remote peer, without reading from it?

It appears the answer to my question is "no, not unless you are willing and able to modify your TCP stack to get access to the necessary private socket-state information".

Since I'm not able to do that, my solution was to redesign the proxy server to always read data from all clients, and throw away any data that arrives from a client whose partner hasn't connected yet. This is non-optimal, since it means that the TCP streams going through the proxy no longer have the stream-like property of reliable in-order delivery that TCP-using programs expect, but it will suffice for my purpose.

Checking if a Socket has closed in C++

Note: I'm assuming the connected socket is communicating over a network link, because I'm not sure how it would become disconnected if it were a local pipe, except by one process or the other dying.

Use the select() function in the socket API to query the read status of the socket. select() will tell you the socket is "readable" if any of the following is true:

  • If listen has been called and a connection is pending, accept will succeed.
  • Data is available for reading (includes OOB data if SO_OOBINLINE is enabled).
  • Connection has been closed/reset/terminated.

So, if select() says that the socket is readable, it is safe to call recv(), which will give you WSACONNRESET or return 0 bytes if the connection was reset or closed respectively. select() takes a 'timeout' parameter, which you can set to an appropriate time interval or zero if you want to poll the socket state.

Information on the select() function is at http://msdn.microsoft.com/en-us/library/ms740141(VS.85).aspx

Problem with select when closing socket

One thing that is wrong in your code is that you're not checking the return value of select.

If select is interrupted by a signal (returns -1 with errno = EINTR, for example SIGCHLD if one of the children died), then the contents of &readfds is undefined, and thus must not be read. (See for example the Linux man page for select.)

So check the return value if select, and loop right back to it without going through the &readfds processing if there's a temporary error like EINTR.

select says socket is ready to read when it is definitely not (actually is closed already)

the socket is "readable" if the remote peer closes it, you need to call recv and handle both the case where it returns an error, and the case where it returns 0, which indicates that the peer shut down the connection in an orderly fashion.

Reading the SO_ERROR sockopt is not the correct way, as it returns the current pending error (from, eg. a non-blocking connect)

Closing server using select function

What you are doing is correct.

The proper way to close a socket, whether it is a connected socket or a listening socket, is with close.



Related Topics



Leave a reply



Submit