Setting timeout to recv function
Asked Answered
F

3

18

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?

Flagitious answered 22/5, 2015 at 11:14 Comment(0)
E
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...
    }
}
Eleaseeleatic answered 22/5, 2015 at 11:38 Comment(2)
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);Drub
Thank you very much, you're right indeed! nfds is the highest-numbered file descriptor + 1.Eleaseeleatic
M
37

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))
        //...
}
Muscovado answered 22/5, 2015 at 17:6 Comment(8)
FYI This solution only works on Windows platforms, it can't be used on Unix/Linux/OSX platforms.Drub
@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). So does OSX.Muscovado
your solution won't work on a Linux/Unix/OSX platform because it contains code fragments like DWORD, WSAGetLastError() which are Windows artifacts.Drub
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.Muscovado
I have updated my answer to account for other platforms.Muscovado
Awesome thanks @Remy Lebeau now I see have two nice solutions to this stackoverflow questions.Drub
does not work on ubuntu 16.04 LTS - wait for timeout is NOT happening, -1 is returned immediatelyUnscientific
@Unscientific Ubuntu supports SO_RCVTIMO, according to the manpage.Muscovado
E
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...
    }
}
Eleaseeleatic answered 22/5, 2015 at 11:38 Comment(2)
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);Drub
Thank you very much, you're right indeed! nfds is the highest-numbered file descriptor + 1.Eleaseeleatic
H
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

Hand answered 22/5, 2015 at 11:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.