20

Can someone provide me an example of how to use select() to see if a client has closed the connection on a socket?

FYI. I'm using linux.

Thanks!

poy
  • 10,063
  • 9
  • 49
  • 74

4 Answers4

21

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;
}
Erik
  • 88,732
  • 13
  • 198
  • 189
  • I'm writing a telnet server and I have it working but I don't do "sock+1" in my select. Why would you need to do that? – James May 02 '17 at 04:01
  • 1
    @James: That is just how the API is defined. – jxh Dec 24 '17 at 16:15
  • All that when a simple `recv()` with the `MSG_PEEK` flag would have done it in one line. – user207421 May 30 '20 at 00:25
  • @user207421 The question was how to check *with select*. Which implies that this check could be a part of a select for multiple other events. Using a `recv()` is not an answer to *this* question, it's a solution to a different problem. – Erik Jun 03 '20 at 20:28
  • tested unusable. always return false even if the client has disconnected – Liang Xiao Aug 08 '22 at 09:49
9

You don't need to do a select() followed by ioctl(). You can instead do a non-blocking peek on the socket to see if it returns 0.

bool isclosed (int sock) {
    char x;
interrupted:
    ssize_t r = ::recv(sock, &x, 1, MSG_DONTWAIT|MSG_PEEK);
    if (r < 0) {
        switch (errno) {
        case EINTR:     goto interrupted;
        case EAGAIN:    break; /* empty rx queue */
        case ETIMEDOUT: break; /* recv timeout */
        case ENOTCONN:  break; /* not connected yet */
        default:        throw(errno);
        }
    }
    return r == 0;
}
jxh
  • 69,070
  • 8
  • 110
  • 193
4

If you get a read notification and the read returns 0 bytes the client has exited cleanly. If not you will get an exception notification.

Dennis Hostetler
  • 614
  • 7
  • 10
  • The client has closed the docket cleanly, or shutdown its output, or exited cleanly on a UNIX or Linux system, but not for example a Windws system, and if not you will get an *error* indication. – user207421 May 30 '20 at 00:27
-1

Here is an example to checks if the socket is closed by peer WITHOUT reading form it in case of non-empty recv buffer.

#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* FOR: POLLRDHUP define */
#endif
#include <poll.h>

bool connection_peer_closed(int fd)
{
    struct pollfd pfd;
    int ret;

    if (fd < 0) {
        return true;
    }

    pfd.fd = fd;
    pfd.events = POLLRDHUP;
    pfd.revents = 0;
    ret = poll(&pfd, 1, 0);
    if (ret > 0 && (pfd.revents & (POLLHUP | POLLERR | POLLRDHUP))) {
        return true;
    } else {
        return false;
    }
}