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
How to Assert If a Std::Mutex Is Locked
Linux C++ Error: Undefined Reference to 'Dlopen'
What Is the Performance Penalty of C++11 Thread_Local Variables in Gcc 4.8
Why Does the Free() Function Not Return Memory to the Operating System
Why Is a NaïVe C++ Matrix Multiplication 100 Times Slower Than Blas
Why Would Connect() Give Eaddrnotavail
Force to Link Against Unused Shared Library
How to Monitor a Folder with All Subfolders and Files Inside
How to Read the Results of a System() Call in C++
String Conversion with Boost Locale: Different Behaviour on Windows and Linux
Is the Gettimeofday Function Thread Safe in Linux
Why Does Poll Keep Returning Although There Is No Input
Are There Any Way to Link My Program with Wine-Compiled Part