2009-04-09 8 views
6

Linuxで私のプロジェクトの1つにブロッキングソケットが使用されています。物事は非常に連続的に起こるので、ノンブロッキングは物事をより複雑にするだけです。とにかく、私は、recv()コールが-1で、errnoEAGAINに設定されていることがよくあることがわかりました。ブロッキングソケットが返されます。EAGAIN

manページでは、ノンブロッキングソケットの場合にこのようなことが実際に言及されています。ノンブロッキングの場合、ソケットは使用可能な場合と使用できない場合がありますので、再試行する必要があります。

ブロッキングソケットの場合はどうなりますか?それを避けるために何かできますか?

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; 

:現時点では

、それに対処するために私のコードは、この(私はそれがエラーに例外をスローしていますが、それを超えて、それはrecv()周りに非常に単純なラッパーである)のようになりますこれは正しいですか?EAGAINの状態がかなり頻繁に発生します。

EDIT:これは関連性があるかもしれないことに気付いたものです。

  1. 私はsetsockopts()を使用してソケットの読み取りタイムアウトを設定するのですが、それは30秒に設定されています。 EAGAINは30秒に1回以上の頻度で起こります。 修正私のデバッグに欠陥があったので、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の両方の呼び出しは正常に戻ります。したがって、この関数が完了すると、ソケットは再びブロックモードになります。

答えて

19

それはあなたがそれはまたのrecvがEAGAIN

+0

はい、それは30000ミリ秒に設定されていますが、私はEAGAINの*方法*をそれより頻繁に得ます。かなり驚異的。 –

+0

*訂正*私のデバッグに欠陥があった、EAGAINは私が思ったほど頻繁に起こらなかった。おそらくタイムアウトがトリガーされます。 –

1

フラグの一部としてMSG_DONTWAITを使用している可能性はありますか? manページには、利用可能なデータがなく、このフラグが指定されている場合に、EAGAINと表示されます。

recv()がやや成功するまでブロックを強制したい場合は、MSG_WAITALLフラグを使用します。

+0

MSG_DONTWAITが使用されていません。 –

0

私はこれを最初の試みとして提案していませんが、すべてのオプションが不足している場合は、常にソケットのselect()を適度に長いタイムアウトでデータ待ちにすることができます。

0

EAGAINを返すために、原因となるよう(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...)経由)ソケットに設定された非ゼロの受信タイムアウトが「おっと!ごめんなさい、ほぼ同じようOSによって生成されている可能性がありますあなたを邪魔するために。 "このエラーが発生した場合は、もう一度お試しください。深刻なまたは致命的なエラーではありません。LinuxやLynxOSでこれらの割り込みが1日に1回から1日に100回まで発生するのがわかりました。

関連する問題