将超时设置为 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 方法上设置超时?