将超时设置为 recv 函数

Posted

技术标签:

【中文标题】将超时设置为 recv 函数【英文标题】:Setting timeout to recv function 【发布时间】:2015-05-22 11:14:04 【问题描述】:

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

【问题讨论】:

【参考方案1】:

另一种在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))
        //...

【讨论】:

仅供参考 此解决方案仅适用于 Windows 平台,不能用于 Unix/Linux/OSX 平台。 @drbobdugan:Windows 不是唯一支持SO_RCVTIMEO 的平台,尽管在其他平台上输入参数通常是timeval 结构。 Linux implements SO_RCVTIMEO (and SO_SNDTIMEO)。 So does OSX. 您的解决方案无法在 Linux/Unix/OSX 平台上运行,因为它包含 DWORD、WSAGetLastError() 等 Windows 工件的代码片段。 在其他平台上,使用errno 而不是WSAGetLastError()。至于我回答的以 Windows 为中心的性质,这是因为该问题被标记为visual-c++,这意味着仅限 Windows 开发。 我已经更新了我的答案以考虑其他平台。【参考方案2】:

您应该检查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...
    

【讨论】:

此代码示例有一个错误...显示为:int rv = select(s, &set, NULL, NULL, &timeout); 的行应为int rv = select(s+1, &set, NULL, NULL, &timeout); 非常感谢,您说的确实对! nfds 是最高编号的文件描述符 + 1。【参考方案3】:

使用 FD_ISSET() 宏来测试是否有数据要读取。如果返回 false,则不要读取。

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

【讨论】:

以上是关于将超时设置为 recv 函数的主要内容,如果未能解决你的问题,请参考以下文章

MFC中socket编程时recv设置超时属性后,如果超时返回值是啥?谢谢大家:)

如何在 python 的 socket recv 方法上设置超时?

windows下对socket的send和recv的超时设置,并附一个简洁明了的socket简单demo

unix / linux中的keepalive超时

linux网络编程中的超时设置

linux socket阻塞recv怎么返回