我在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()在套接字上设置了读取超时,但是将其设置为30秒。 EAGAIN发生的频率比每30秒发生一次的频率高。 更正我的调试存在缺陷,EAGAIN的发生频率不像我想象的那样频繁。也许是超时触发。
  • 对于连接,我希望能够有连接超时,因此我将套接字临时设置为非阻塞。该代码如下所示:
    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()调用均成功返回,因此该函数完成后,套接字应再次以阻塞模式结束。

    最佳答案

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

    关于c++ - 堵头返回EAGAIN,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/735249/

    10-09 07:12