我在Linux上的一个项目使用阻塞套接字。事情发生非常连续,所以非阻塞只会让事情变得更复杂。无论如何,我发现recv()
来电常常-1
来回errno
,EAGAIN
设置为man
。
recv()
页面实际上只提到非阻塞套接字发生这种情况,这是有道理的。如果没有阻塞,则套接字可能可用,也可能不可用,因此您可能需要重试。
阻塞套接字会导致什么情况发生?我可以做些什么来避免它吗?
目前,我处理它的代码看起来像这样(我在错误时抛出异常,但除此之外它是int ret;
do {
ret = ::recv(socket, buf, len, flags | MSG_NOSIGNAL);
} while(ret == -1 && errno == EAGAIN);
if(ret == -1) {
throw socket_error(strerror(errno));
}
return ret;
的一个非常简单的包装器):
EAGAIN
这是否正确? setsockopts()
条件会经常受到影响。
编辑:我注意到的一些可能相关的事情。
我使用EAGAIN
在套接字上设置了读取超时,但设置为30秒。 EAGAIN
的发生方式多于每30秒发生一次。 更正我的调试存在缺陷,int error = 0;
fd_set rset;
fd_set wset;
int n;
const SOCKET sock = m_Socket;
// set the socket as nonblocking IO
const int flags = fcntl (sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
errno = 0;
// we connect, but it will return soon
n = ::connect(sock, addr, size_addr);
if(n < 0) {
if (errno != EINPROGRESS) {
return -1;
}
} else if (n == 0) {
goto done;
}
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_SET(sock, &rset);
FD_SET(sock, &wset);
struct timeval tval;
tval.tv_sec = timeout;
tval.tv_usec = 0;
// We "select()" until connect() returns its result or timeout
n = select(sock + 1, &rset, &wset, 0, timeout ? &tval : 0);
if(n == 0) {
errno = ETIMEDOUT;
return -1;
}
if (FD_ISSET(sock, &rset) || FD_ISSET(sock, &wset)) {
socklen_t len = sizeof(error);
if (getsockopt(SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
return -1;
}
} else {
return -1;
}
done:
// We change the socket options back to blocking IO
if (fcntl(sock, F_SETFL, flags) == -1) {
return -1;
}
return 0;
并不像我想象的那样经常发生。也许是超时触发。
为了连接,我希望能够连接超时,所以我暂时将套接字设置为非阻塞。该代码如下所示:
fcntl()
我的想法是我将其设置为非阻塞,尝试连接并在套接字上选择,以便我可以强制执行超时。 set和restore {{1}}调用都成功返回,因此当此函数完成时,套接字应该再次以阻塞模式结束。
答案 0 :(得分:19)
您可能在套接字上设置了非零接收超时(通过setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...)
),因为这也会导致recv返回EAGAIN
答案 1 :(得分:1)
您是否有可能将MSG_DONTWAIT
用作旗帜的一部分?如果没有可用的数据且指定了此标志,man
页面将显示EAGAIN
。
如果你真的想在recv()
稍微成功之前强行阻止,你可能希望使用MSG_WAITALL
标志。
答案 2 :(得分:0)
我不建议将其作为首次尝试修复,但是如果你的选项都没有,那么你可以在套接字上select()
使用相当长的超时来强制它等待数据。
答案 3 :(得分:0)
EAGAIN
几乎就像是“哎呀!我很抱歉打扰你。”如果出现此错误,您可以尝试再次阅读,这不是严重或致命的错误。我已经看到这些中断发生在Linux和LynxOS中,每天一次,每天一次。