阻塞套接字返回EAGAIN

时间:2009-04-09 17:51:02

标签: c++ c linux sockets

我在Linux上的一个项目使用阻塞套接字。事情发生非常连续,所以非阻塞只会让事情变得更复杂。无论如何,我发现recv()来电常常-1来回errnoEAGAIN设置为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()条件会经常受到影响。

编辑:我注意到的一些可能相关的事情。

  1. 我使用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; 并不像我想象的那样经常发生。也许是超时触发。

  2. 为了连接,我希望能够连接超时,所以我暂时将套接字设置为非阻塞。该代码如下所示:

    fcntl()
  3. 我的想法是我将其设置为非阻塞,尝试连接并在套接字上选择,以便我可以强制执行超时。 set和restore {{1}}调用都成功返回,因此当此函数完成时,套接字应该再次以阻塞模式结束。

4 个答案:

答案 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中,每天一次,每天一次。