2

TCP ソケットで奇妙なバグに遭遇しました。SO_KEEPALIVEデフォルトですべてのソケットで有効になっているようです。

ソケットを作成してサーバーに接続するための短いテスト ケースを作成しました。接続直後、 でSO_KEEPALIVE確認getsockopt。値はゼロ以外で、MSDN によると、キープアライブが有効になっていることを意味します。多分私はこれを誤解しています。

最近、サーバーが 2 回連続で切断されるという奇妙なバグが発生しました。一部のクライアントは、ログオン情報を送信して応答を待っている状態でした。サーバーに接続されたソケットにオーバーラップが投稿されたにもかかわらずWSARecv、サーバーがクラッシュしたことをクライアントに通知するための完了が投稿されなかったため、ソケットが完全に閉じられていないと想定しています。

約 2 時間後 (実際には約 1 時間 59 分 19 秒) に、読み取りの完了パケットが送信され、接続が開かれていないことがクライアントに通知されました。これが私が疑い始めたところSO_KEEPALIVEです。

なぜこれが起こったのか理解しようとしています。何らかの理由で接続を失ったクライアントは自動的にサーバーに再接続するはずなので、少し問題が発生しました。この場合、切断が通知されなかったため、クライアントは 2 時間後まで再接続しませんでした。

明らかな解決策はタイムアウトを設定することですが、この状況がどのように発生するかを知りたいです。

SO_KEEPALIVEアプリケーションサーバーまたはクライアントによってソケットに設定されていません。

// Error checking is removed for this snippet, but all winsock calls succeed.
int main() {
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(2, 2);
    err = WSAStartup(wVersionRequested, &wsaData);

    SOCKET foo = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);

    DWORD optval;
    int optlen = sizeof(optval);
    int test = 0;
    test = getsockopt(foo, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, &optlen);
    std::cout << "Returned " << optval << std::endl;

    sockaddr_in clientService; 
    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
    clientService.sin_port = htons(446);

    connect(foo, (SOCKADDR*) &clientService, sizeof(clientService));

    test = getsockopt(foo, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, &optlen);
    std::cout << "Returned " << optval << std::endl;

    std::cin.get();
    return 0;
}

// Example output:
// Returned 2883584
// Returned 2883584
4

1 に答える 1

5

最初に、VM 上のオペレーティング システムのクリーン インストールでテストを実行します。おそらく、あなたがインストールした他の何かがキープアライブ設定をいじったのではないかと思います。

第二に、キープアライブが有効になっていることが問題の原因であるとは思えません。キープアライブが有効になっていない場合、その保留中の読み取りから接続閉鎖通知を受け取ることはありません。TCP はそのように機能するはずです。これにより、中間ルーターが離れて戻ってきても、知ることも気にすることもありません。失敗が通知されるのは、送信しようとして接続が切断された場合 (または、この場合、送信しようとしてサーバーがバウンスした場合) だけです。キープアライブが有効になっているということは、その 1 時間 59 分で TCP スタックがキープアライブを送信し、接続がダウンしていることに気付いたことを意味します。キープアライブが有効になっていない場合は、何かを送信するまで待たなければなりませんでした。

クライアントが接続がダウンしたかどうかを知る必要がある場合は、キープアライブを完全に無視することをお勧めします (ご覧のとおり、キープアライブを有効にした人でなくてもマシン全体に影響を与えます。 )。可能であれば、アプリケーション レベルの ping やタイムアウトをプロトコルに追加します。したがって、おそらく、すべてのコマンドは 30 秒以内の応答を期待し、サーバーから 1 分ごとに を送信します...その後、好きなだけすぐに切断された接続を見つけ、その時点で切断して再接続できます。

サーバーフレームワークでこれをかなりうまく使用しました。実際、標準の「非同期読み取りタイムアウト」接続フィルター「接続再確立」フィルターを使用すると、接続が常に有効であることを簡単に確認できます。読み取りタイムアウトが行うのは、既存の接続を中止することだけであり、接続が他の理由で閉じられた場合と同様に、接続再確立コードが起動して接続を再作成します。

于 2011-02-08T07:29:26.643 に答える