一尘不染

堵头返回EAGAIN

linux

我在Linux上的一个项目使用阻塞套接字。事情是非常连续地发生的,因此非阻塞只会使事情变得更加复杂。无论如何,我发现经常一个recv()调用返回-1errno设置为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()调用均成功返回,因此此功能完成后,套接字应再次以阻塞模式结束。


阅读 300

收藏
2020-06-03

共1个答案

一尘不染

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

2020-06-03