6

私の問題は、recv() 呼び出しにあるスレッドがあることです。リモート ホストが (close() ソケット呼び出しなしで) 突然終了し、recv() 呼び出しが引き続きブロックされます。これは明らかに良くありません。スレッドに参加してプロセスを (ローカルで) 閉じるとき、このスレッドは二度と来ない recv を待っているため、決して終了しないからです。

だから私の質問は、人々がこの問題に対処するための最良の方法であると一般的に考えている方法は何ですか? 回答する前に知っておくべき注意事項がいくつかあります。

  • リモートホストが終了する前にソケットを閉じることを保証する方法はありません。

  • このソリューションでは、外部ライブラリ (boost など) を使用できません。C++/C の標準ライブラリ/機能を使用する必要があります (C++0x 固有ではないことが望ましい)。

これは過去に尋ねられた可能性が高いことは知っていますが、この問題を適切に修正する方法について誰かに理解してもらいたいと思います(過去に私が行ったであろう非常にハックなことをせずに)。

ありがとう!

4

4 に答える 4

5

ブロッキング ソケットを引き続き使用する場合は、SO_RCVTIMEOsocket オプションを使用できます。

   SO_RCVTIMEO and SO_SNDTIMEO
          Specify the receiving or sending  timeouts  until  reporting  an
          error.   The parameter is a struct timeval.  If an input or out-
          put function blocks for this period of time, and data  has  been
          sent  or received, the return value of that function will be the
          amount of data transferred; if no data has been transferred  and
          the  timeout has been reached then -1 is returned with errno set
          to EAGAIN or EWOULDBLOCK just as if the socket was specified  to
          be  nonblocking.   If  the  timeout is set to zero (the default)
          then the operation will never timeout.

したがって、受信を開始する前に:

struct timeval timeout = { timo_sec, timo_usec };
int r = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
assert(r == 0); /* or something more user friendly */

ノンブロッキング I/O を使用する場合はpoll()select()epoll()kqueue()、またはシステムに適したイベント ディスパッチ メカニズムを使用できます。ノンブロッキング I/O を使用する必要がある理由は、システム コールがrecv()返されて、ソケットの入力キューにデータがないことを通知できるようにする必要があるためです。使用する例は、もう少し複雑です。

for (;;) {
    ssize_t bytes = recv(s, buf, sizeof(buf), MSG_DONTWAIT);
    if (bytes > 0) { /* ... */ continue; }
    if (bytes < 0) {
        if (errno == EWOULDBLOCK) {
            struct pollfd p = { s, POLLIN, 0 };
            int r = poll(&p, 1, timo_msec);
            if (r == 1) continue;
            if (r == 0) {
                /*...handle timeout */
                /* either continue or break, depending on policy */
            }
        }
        /* ...handle errors */
        break;
    }
    /* connection is closed */
    break;
}
于 2013-06-21T02:42:17.860 に答える
2

TCP キープアライブ プローブを使用して、リモート ホストがまだ到達可能かどうかを検出できます。キープアライブが有効になっている場合、接続が長時間アイドル状態であった場合、OS はプローブを送信します。リモート ホストがプローブに応答しない場合、接続は閉じられます。

Linux では、socket オプションを設定することでキープアライブ プローブを有効にできます。また、、、およびsocket オプションを使用SO_KEEPALIVEしてキープアライブのパラメーターを構成できます。それらの詳細については、 とを参照してください。TCP_KEEPCNTTCP_KEEPIDLETCP_KEEPINTVLtcp(7)socket(7)

Windows もSO_KEEPALIVEソケット オプションを使用してキープアライブ プローブを有効にしますが、キープアライブ パラメーターを構成するには、SIO_KEEPALIVE_VALSioctlを使用します。

于 2013-06-21T03:16:16.533 に答える
0

select() を使用できます

http://linux.die.net/man/2/selectから

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

select() は、1 つ以上のファイル記述子で最初のイベント (読み取り準備完了、書き込み準備完了、または例外) が発生するか、タイムアウトが発生するまでブロックします。

于 2013-06-21T02:44:44.387 に答える
0

ソケットでありselect、おそらく理想的な選択肢です。バックアップとして考慮する必要がある追加のオプションは、プロセスにシグナルを送信することです (たとえば、alarm()呼び出しを使用)。これにより、進行中のシステムコールが強制的に終了され、 に設定さerrnoEINTRます。

于 2013-06-21T02:46:13.753 に答える