阻塞套接字返回EAGAIN

Eva*_*ran 7 c c++ sockets 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;
Run Code Online (Sandbox Code Playgroud)

这甚至是正确的吗?这种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;
    
    Run Code Online (Sandbox Code Playgroud)

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

Has*_*kun 19

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