2009-04-09 65 views
6

我在Linux上的一个项目使用阻塞套接字。事情发生的非常连续,所以非阻塞会让事情变得更加复杂。无论如何,我发现通常recv()呼叫返回-1errno设置为EAGAIN阻塞套接字返回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秒。每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; 
    

的想法是,我将它设置为非阻塞,尝试连接,并选择在插座上,所以我可以强制超时。设置和恢复fcntl()调用都会​​成功返回,所以当此函数完成时,套接字应该再次处于阻止模式。

回答

19

这有可能是你有一个非零接收超时套接字上设置(通过setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...))的,这也将导致recv的返回EAGAIN

+0

是的,但它设置为30000毫秒,我比EAGAIN的*路*更频繁。非常常态。 – 2009-04-09 18:24:12

1

是否有可能使用MSG_DONTWAIT被指定为标志的一部分? man页面说EAGAIN将发生如果没有数据可用,并且此标志被指定。

如果你真的想强制一个块,直到recv()有点成功,你可能希望使用MSG_WAITALL标志。

+0

我只是grepped我的源码树,MSG_DONTWAIT没有使用。 – 2009-04-09 18:09:41

0

我不建议将此作为第一次尝试修复,但如果您全部超出选项,则可以在套接字上始终使用select(),并且超时时间相当长,以强制它等待数据。

0

EAGAIN由OS产生几乎像一个“糟糕!我很抱歉打扰你。“如果出现此错误,您可以尝试再次阅读,这不是严重或致命的错误。我发现这些中断在Linux和LynxOS中每天都会发生,一天发生100次。