为 recv 函数设置超时

Setting timeout to recv function

我使用 recv 函数从套接字读取数据。当没有可供阅读的数据时,我遇到了问题。我的程序刚刚停止。我发现我可以使用 select 函数设置超时。但是看起来超时会影响 select 函数本身,并且 select 之后的 recv 仍然不连续地等待。

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)
      {
      ...
      }

超时后如何询问 recv 函数 return?

使用FD_ISSET()宏测试是否有数据可读。如果它 returns 为假,则不要读取。

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

您应该检查 select 的 return 值。 select 将 return 0 以防超时过期,因此您应该检查错误并仅在 select return 为正值时才调用 recv

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...
    }
}

另一种在 recv() 本身上设置超时而不使用 select() 的方法是使用 setsockopt() 设置套接字的 SO_RCVTIMEO 选项(在支持它的平台上) .

在 Windows 上,代码如下所示:

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)
        //...
}

在其他平台上,代码将如下所示:

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))
        //...
}