5

最近、私のソフトウェアの数人のユーザーがWindows 8では機能しないと言ってきました。調査の結果、奇妙な理由で、サーバーソケットが常に接続を受け入れるとは限らないが、タイムアウトすることが判明しました。 。

さらに奇妙なことに、リモートホストに接続するときだけでなく、ローカルホストに接続するときにも発生します。

「何を試しましたか?」

  • 明らかなこと:ファイアウォールをオフにして(効果なし)、他のソフトウェアが機能するかどうかを確認し(機能する)、コードのランダムな行を変更してみてください(効果なし)。
  • あまり目立たないもの:クライアントまたはサーバーインスタンスごとに1つではなく、グローバルWSAStartupを使用します(効果はありません)。

注意:まったく同じコードがWindowsXPとWindows7で正常に機能し、ローカルホスト接続にも影響します(ハードウェアの問題ではありません)。また、接続の3分の1だけが失敗し、残りは正常に機能します。

さて、これらすべての単語よりもはるかに便利なので、実際のコードをいくつか紹介します。

ソケットのセットアップ:

int iResult;

struct addrinfo *result = NULL;
struct addrinfo hints;

ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;

// "Resolve" our localhost
iResult = getaddrinfo(NULL, port, &hints, &result);
if (iResult != 0) {
    printf("error (2) : %d\n", iResult);
    return false;
}

// Create the socket
listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (listenSocket == INVALID_SOCKET) {
    freeaddrinfo(result);
    printf("error (3) : %d\n", WSAGetLastError());
    return false;
}

// Bind it
iResult = bind(listenSocket, result->ai_addr, result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
    freeaddrinfo(result);
    closesocket(listenSocket);
    printf("error (4) : %d\n", WSAGetLastError());
    return false;
}

freeaddrinfo(result);

// Listen
iResult = listen(listenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
    closesocket(listenSocket);
    printf("%d\n", WSAGetLastError());
    return false;
}

おそらくお分かりのように、MSDNからほぼ直接取得されており、問題ないはずです。その上、接続の2/3で機能するので、セットアップコードに問題があるのではないかと思います。

受信者コード:

    if (listenSocket == INVALID_SOCKET) return false;
#pragma warning(disable:4127)
    fd_set fds;

    SOCKET client;
    do {
        FD_ZERO(&fds);
        FD_SET(listenSocket, &fds);

        struct timeval timeout;
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;
        if (!select(1, &fds, NULL, NULL, &timeout)) continue; // See you next loop!

        struct sockaddr_in addr;
        socklen_t addrlen = sizeof(addr);

        // Accept the socket
        client = accept(listenSocket, (struct sockaddr *)&addr, &addrlen);

        if (client == INVALID_SOCKET) {
            printf("[HTTP] Invalid socket\n");
            closesocket(listenSocket);
            return false;
        }

        // Set a 1s timeout on recv()
        struct timeval tv;
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv));

        // Receive the request
        char recvbuf[513];
        int iResult;
        std::stringbuf buf;

        clock_t end = clock() + CLOCKS_PER_SEC; // 1s from now

        do {
            iResult = recv(client, recvbuf, 512, 0);
            if (iResult > 0) {
                buf.sputn(recvbuf, iResult);
            } else if (iResult == 0) {
                // Hmm...
            } else {
                printf("[HTTP] Socket error: %d\n", WSAGetLastError());
                break;
            }
        } while (!requestComplete(&buf) && clock() < end);

このコードは「[HTTP]Socketerror:10060」エラーを吐き出すので、その後に続くコードはまったく関係ありません。

select実際のループは他のことも行うため、この呼び出しがありますが、ソケットに関連していないため、省略しました。

さらに見知らぬ人:Wiresharkによると、Windowsは実際のネットワークエラーを起こしているようです:http://i.imgur.com/BIrbD.png

私はしばらくの間これを理解しようとしてきました、そして私はおそらくただ愚かなことをしているだけなので、私はあなたのすべての答えに本当に感謝しています。

4

2 に答える 2

4

Windows では、SO_RCVTIMEO オプションは MILLISECONDS で DWORD 引数を必要としますが、timeval 構造体は必要としません。http://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspxを参照してください。

timeval を渡すと、ウィンドウはそれを DWORD として解釈し、seconds メンバーはミリ秒として読み取られます。Windows 8 より前のバージョンで timeval 引数が機能した理由はわかりません。おそらく、Windows 8 で削除された文書化されていない機能でした。

于 2013-03-28T07:22:03.197 に答える
4

私はこの厄介な問題に丸一日取り組んできましたが、サーバー全体をゼロから書き直し、別の方法で実装することで、最終的に解決することができました。問題を追跡して、もううまくsetsockoptいかないように思われるためSO_RCVTIMEO、タイムアウトがゼロ秒になり、ランダム接続がタイムアウトします。

私の新しい実装はタイムアウトを使用しなくなり、単純にノンブロッキングで非同期になりました。非常にうまく機能しますが、より多くのコードが必要です。

Windows 8 の単なるバグであり、リリース前の更新プログラムで修正されると思います。Microsoft が Berkeley Sockets API をこのように変更したかったとは思えません。

于 2012-10-05T15:46:14.123 に答える