17

I read from socket using recv function. I have problem when no data available for reading. My programm just stops. I found that I can set timeout using select function. But looks that timeout affects select function itself and recv that goes after select still waits uncontinuously.

fd_set set;
struct timeval timeout;
FD_ZERO(&set); /* clear the set */
FD_SET(s, &set); /* add our file descriptor to the set */
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC;
timeout.tv_usec = 0;
int rv = select(s, &set, NULL, NULL, &timeout);
if((recv_size = recv(s , rx_tmp , bufSize ,0)) == SOCKET_ERROR)
      {
      ...
      }

How to ask recv function return after some timout?

vico
  • 17,051
  • 45
  • 159
  • 315

3 Answers3

36

Another way to set a timeout on recv() itself without using select() is to use setsockopt() to set the socket's SO_RCVTIMEO option (on platforms that support it).

On Windows, the code would look like this:

DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

//...

recv_size = recv(s, rx_tmp, bufSize, 0);
if (recv_size == SOCKET_ERROR)
{
    if (WSAGetLastError() != WSAETIMEDOUT)
        //...
}

On other platforms, the code would look like this instead:

struct timeval timeout;
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC;
timeout.tv_usec = 0;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));

//...

recv_size = recv(s, rx_tmp, bufSize, 0);
if (recv_size == -1)
{
    if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
        //...
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    FYI This solution only works on Windows platforms, it can't be used on Unix/Linux/OSX platforms. – drbobdugan Apr 28 '16 at 00:11
  • 1
    @drbobdugan: Windows is not the only platform that supports `SO_RCVTIMEO`, though on other platforms the input parameter is usually a `timeval` struct instead. [Linux implements `SO_RCVTIMEO` (and `SO_SNDTIMEO`)](http://man7.org/linux/man-pages/man7/socket.7.html). [So does OSX](https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setsockopt.2.html). – Remy Lebeau Apr 28 '16 at 01:06
  • your solution won't work on a Linux/Unix/OSX platform because it contains code fragments like DWORD, WSAGetLastError() which are Windows artifacts. – drbobdugan Apr 28 '16 at 01:19
  • 3
    On other platforms, use `errno` instead of `WSAGetLastError()`. As for the Windows-centric nature of my answer, it is because the question is tagged with `visual-c++`, which means Windows-only development. – Remy Lebeau Apr 28 '16 at 01:32
  • 1
    I have updated my answer to account for other platforms. – Remy Lebeau Apr 28 '16 at 01:37
  • Awesome thanks @Remy Lebeau now I see have two nice solutions to this stackoverflow questions. – drbobdugan Apr 28 '16 at 11:02
  • does not work on ubuntu 16.04 LTS - wait for timeout is NOT happening, -1 is returned immediately – serup Jun 23 '16 at 07:31
  • @serup Ubuntu supports `SO_RCVTIMO`, according to the [manpage](http://manpages.ubuntu.com/manpages/wily/man2/getsockopt.2freebsd.html). – Remy Lebeau Jun 23 '16 at 14:56
14

You should check return value of select. select will return 0 in case timeout expired, so you should check for error and call recv only if select returned positive value:

On success, select() and pselect() return the number of file descriptors contained in the three returned descriptor sets (that is, the total number of bits that are set in readfds, writefds, exceptfds) which may be zero if the timeout expires before anything interesting happens.

int rv = select(s + 1, &set, NULL, NULL, &timeout);
if (rv == SOCKET_ERROR)
{
    // select error...
}
else if (rv == 0)
{
    // timeout, socket does not have anything to read
}
else
{
    // socket has something to read
    recv_size = recv(s, rx_tmp, bufSize, 0);
    if (recv_size == SOCKET_ERROR)
    {
        // read failed...
    }
    else if (recv_size == 0)
    {
        // peer disconnected...
    }
    else
    {
        // read successful...
    }
}
Nemanja Boric
  • 21,627
  • 6
  • 67
  • 91
  • 1
    This code example has an error... the line that reads: `int rv = select(s, &set, NULL, NULL, &timeout);` should read `int rv = select(s+1, &set, NULL, NULL, &timeout);` – drbobdugan Apr 27 '16 at 22:34
  • Thank you very much, you're right indeed! `nfds` is the highest-numbered file descriptor + 1. – Nemanja Boric Apr 28 '16 at 08:59
1

use the FD_ISSET() macro to test whether there is data to read. If it returns false, don't do the read.

http://linux.die.net/man/3/fd_set

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142