【问题标题】:Blocking socket returns EAGAIN阻塞套接字返回 EAGAIN
【发布时间】:2010-10-18 15:02:58
【问题描述】:

我在 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 条件经常被命中。

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

  1. 我确实使用setsockopts() 在套接字上设置了读取超时,但它设置为 30 秒。 EAGAIN 的发生频率超过每 30 秒一次。 更正我的调试存在缺陷,EAGAIN 的发生频率并没有我想象的那么频繁。可能是超时触发。

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

    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;
    

我的想法是我将它设置为非阻塞,尝试连接并在套接字上选择,以便我可以强制超时。 set 和 restore fcntl() 调用都成功返回,所以当这个函数完成时,套接字应该再次进入阻塞模式。

【问题讨论】:

    标签: c++ c linux sockets


    【解决方案1】:

    您是否有可能使用MSG_DONTWAIT 被指定为您的标志的一部分? man 页面显示EAGAIN 将在没有可用数据且指定此标志时出现。

    如果您真的想在recv() 有点成功之前强制阻止,您可能希望使用MSG_WAITALL 标志。

    【讨论】:

    • 我刚刚抓取了我的源代码树,没有使用 MSG_DONTWAIT。
    【解决方案2】:

    我不建议将此作为第一次尝试修复,但如果您没有任何选择,您始终可以在套接字上 select() 并设置相当长的超时时间以强制它等待数据。

    【讨论】:

      【解决方案3】:

      您可能在套接字上设置了非零接收超时(通过setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...)),因为这也会导致recv 返回EAGAIN

      【讨论】:

      • 是的,但它设置为 30000 毫秒,我得到 EAGAIN 的 方式 的次数比这更多。几乎是恒定的。
      • CORRECTION 我的调试有缺陷,EAGAIN 并没有我想象的那么频繁。也许是超时触发。
      • 对于任何想要参考的人,check here 并搜索“SO_RCVTIMEO”。
      【解决方案4】:

      EAGAIN 由操作系统生成,几乎就像“糟糕!很抱歉打扰您。”。如果出现此错误,您可以尝试再次阅读,这不是严重或致命错误。我已经看到这些中断在 Linux 和 LynxOS 中发生,从一天一次到一天 100 次不等。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-26
        • 2013-06-04
        • 2016-08-01
        相关资源
        最近更新 更多