将接收函数的超时时间设置为多少?

17

我使用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函数返回?

3个回答

36

如果不使用 select(),另一种在 recv() 上设置超时的方法是使用 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))
        //...
}

1
FYI,此解决方案仅适用于Windows平台,无法在Unix/Linux/OSX平台上使用。 - drbobdugan
1
@drbobdugan:Windows不是唯一支持SO_RCVTIMEO的平台,但在其他平台上,输入参数通常是一个timeval结构。Linux实现了SO_RCVTIMEO(和SO_SNDTIMEOOSX也是如此 - Remy Lebeau
你的解决方案在Linux/Unix/OSX平台上无法运行,因为它包含像DWORD、WSAGetLastError()这样的Windows特有代码片段。 - drbobdugan
3
在其他平台上,请使用errno代替WSAGetLastError()。至于我的答案针对Windows的本质,是因为该问题标记为visual-c ++,这意味着仅限于Windows开发。 - Remy Lebeau
1
我已经更新了我的答案,以考虑其他平台。 - Remy Lebeau
显示剩余3条评论

14

你应该检查select的返回值。如果超时到期,select将返回0,因此您应该检查错误并仅在select返回正值时调用recv

成功时,select()pselect()返回三个返回描述符集中包含的文件描述符数(也就是在readfds、writefds、exceptfds中被设置的总位数),如果在发生任何有趣的事情之前超时到期,则可以为零。

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

1
这个代码示例有一个错误...读取的那一行应该是:int rv = select(s+1, &set, NULL, NULL, &timeout);,而不是int rv = select(s, &set, NULL, NULL, &timeout); - drbobdugan
非常感谢,你说得对!nfds是最高编号的文件描述符加1。 - Nemanja Boric

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接