2009-04-09 95 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次。