2017-08-22 11 views
-2

シンプルな非同期のclientHandlerを実行しているwinsock2を使用する単純なブロッキングサーバー/クライアントアプリケーションがあります。アイデアは、ロボットアプリケーションのキープアライブ記号としてオープンな接続を持つことです。C++自身のソケットサーバーとクライアントが8つのメッセージの後にエラー10054と10053で接続をリセットしました

ソケット(クライアント&サーバ)の初期化とそれらの間の接続の確立はうまくいきますが、8つのメッセージの後に受信側が接続を閉じます(エラー10053)ので、送信側でエラー10054が発生します。すべてのメッセージを手作業で(1分ごとに1回)送信するか、自動的に送信するかは問題になりません。相手側がメッセージの間で返信しても、結果は常に同じです。

メッセージは、メタ情報付きヘッダー(id、command、payloadLength in bytes)と、もしあればacutalペイロードの2つの部分で構成されています。困惑部分は次のようなものです。sizeof()が返すより1バイト以上送る必要があります。それ以外の場合は動作しません。

これまでのところ私が試した:ループ

    • 読み出し部分のメッセージは、複数のクライアントハンドラと任意のコールバックを取り除くソケット
    • への書き込み/読み込みの前にselect()の出力を見て
    • 同じマシンと異なるマシンのクライアント/サーバーで実行中
    • wiresharkを使用した生データの表示

    受信者がRSTフラグを設定しているメッセージの制限まですべてが正常に見えました。クライアントとサーバーの両方の入出力バッファは65kbに設定されており、私のメッセージは近づかない。さらに、私はメッセージごとに巨大なデータセット(50kb)を送るときにも起こります。接続が終了すると、8つのメッセージが表示されます。

    私は、ソケットの構成のどこかに明らかなエラーがあると仮定しますが、エラー10054は非常にあいまいであるため、ヘルプやヒントは非常に不便です。

    ここには、サーバー/クライアントを構成し、メッセージを送受信するコードスニペットがあります。読みやすくするために私はここにすべてのエラーチェックを追加しないでください:クライアント用として

    //Server 
    
    //Init 
    WSADATA wsa; 
    WSAStartup(MAKEWORD(2, 2), &wsa); 
    
    struct sockaddr_in config; 
    config.sin_family = AF_INET; 
    config.sin_addr.s_addr = INADDR_ANY; 
    config.sin_port = htons(8080); 
    
    Socket m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    
    //set server to a state where it accepts incoming connections 
    bind(m_sock, (sockaddr *)&config, sizeof(sockaddr_in)); 
    listen(m_sock, 100); 
    
    int c = sizeof(struct sockaddr_in); 
    //block until new client connects 
    Socket m_client = accept(m_sock, (sockaddr*)client, &c)); 
    
    //handling and awaiting the client messages would have happend asyncronously with the m_client pointers swapped etc. 
    //receive data 
    struct msgHeader{ 
        short id; 
        short command; 
        short payloadLength; 
    } 
    
    
    msgHeader* head = new msgHeader; 
    int error = 0; 
    do { 
         err = recv(*client, (char*)head, headerSize, SD_RECEIVE); 
    
         if (err == headerSize) 
         { 
          if (head->payloadLength > 0) 
          { 
    
           char* payload = new char[head->payloadLength]{0}; 
    
           err = recv(*client, payload, head->payloadLength, SD_RECEIVE); // SERVER DIES HERE!! 
           if (err == head->payloadLength) 
           { 
          // callback is happening here 
           } 
         else 
         { 
          // error occured 
         } 
          } 
         } 
        } while (err > 0); 
    
    delete head; 
    closesocket(m_sock); 
    WSACleanup(); 
    

    を:

    WSADATA wsa; 
    WSAStartup(MAKEWORD(2, 2), &wsa)); 
    
    
    m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    
    m_serverConfig.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    m_serverConfig.sin_family = AF_INET; 
    m_serverConfig.sin_port = htons("8080");  
    connect(m_sock, (struct sockaddr *)&m_serverConfig, sizeof(sockaddr_in)); 
    
    std::string msg = "Hello from client"; 
    msgHeader head{ 23, 0, msg.size() }; 
    
    while(true) 
    { 
    
        send(m_sock, (char*)&head, sizeof(msgHeader) + 1, SD_SEND); //this +1 offset irretates me since I'm trying to send raw data. sizeof returns 6 which is ok only if I send 7 bytes the server receives 6 thus the complete header. 
        send(m_sock, msg.c_str(), msg.size() + 1, SD_SEND)); // CLIENT DIES HERE AFTER THE 8th iteration!! 
    } 
    
    //closesocket()... wsacleanup etc... 
    

    あなたが全体のコードが必要な場合は、私はレポを公開すると、リンクを投稿します。

    これまでのところ、バグを探す場所を提案してくれてありがとう。

    EDIT:クライアントとサーバー+(屋根裏プロジェクト)、それをコンパイルするのに必要なすべてのための コードはここで見つけることができます: Code

  • +0

    TCPストリームではなく、メッセージ、ベースのプロトコルです。 'err = recv(* client、(char *)head、headerSize、SD_RECEIVE);'は利用可能なものに依存して* 'headerSize'バイトまで*与えます。これはすべてローカルホストなので、あなたはおそらく幸運になるでしょうが、それには期待できません。 'SD_RECEIVE'はシャットダウン受信フラグです。これは 'recv'のための有効な入力ではありません。 'send'呼び出しで' SD_SEND'(シャットダウン送信)をします。 – user4581301

    +0

    私は 'MSG_WAITALL'フラグも試しました。このフラグは' headerSize'が受信され、同じ結果が返されるまでブロックします。 Nontheless:最初の7つのメッセージすべてが正しく受信されるため、ここではこれが主要な問題ではありません。 – Stanislav

    +0

    'MSG_WAITALL'はストリームであり、メッセージではないので、TCPでは動作しません。 – user4581301

    答えて

    0

    私はこのコードでは多くのミスを参照してください、不足している少なくともそのうち/不適切なエラー処理。

    以下のようなものを試してみてください。

    サーバー:

    WSADATA wsa; 
    int res = WSAStartup(MAKEWORD(2, 2), &wsa); 
    if (res != 0) { 
        // error handling ... 
    } 
    
    SOCKET m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    if (m_sock == INVALID_SOCKET) { 
        // error handling ... 
    } 
    
    struct sockaddr_in config = {0}; 
    config.sin_family = AF_INET; 
    config.sin_addr.s_addr = INADDR_ANY; 
    config.sin_port = htons(8080); 
    
    if (bind(m_sock, (sockaddr *)&config, sizeof(config)) == SOCKET_ERROR) { 
        // error handling ... 
    } 
    
    if (listen(m_sock, 100) == SOCKET_ERROR) { 
        // error handling ... 
    } 
    
    struct sockaddr_in client = {0}; 
    int c = sizeof(client); 
    
    SOCKET m_client = accept(m_sock, (sockaddr*)&client, &c); 
    if (m_client == INVALID_SOCKET) { 
        // error handling... 
    } 
    
    ... 
    
    #pragma pack(push, 1) // or your compiler's equivalent 
    struct msgHeader { 
        short id; 
        short command; 
        short payloadLength; 
    }; 
    #pragma pack(pop) 
    
    int recvAll(SOCKET s, void *buf, int buflen) 
    { 
        int res = recv(s, buf, buflen, MSG_WAITALL); 
        if ((res != SOCKET_ERROR) || (WSAGetLastError() != WSAEOPNOTSUPP)) { 
         return res; 
        } 
        char *pbuf = (char*) buf; 
        int len = buflen; 
        while (len > 0) { 
         res = recv(s, buf, len, 0); 
         if (res <= 0) { 
          return res; 
         } 
         pbuf += res; 
         len -= res; 
        } 
        return buflen; 
    } 
    
    msgHeader head; 
    std::vector<char> payload; 
    int res; 
    
    do { 
        res = recvAll(*client, &head, sizeof(head)); 
        if (res <= 0) { 
         if (res == SOCKET_ERROR) { 
          // error handling ... 
         } else { 
          // client disconnected 
         } 
         break; 
        } 
    
        head.id = htons(head.id); 
        head.command = htons(head.command); 
        head.payloadLength = htons(head.payloadLength); 
    
        if (head.payloadLength > 0) 
        { 
         payload.resize(head.payloadLength); 
    
         res = recvAll(*client, &payload[0], head.payloadLength); 
         if (res <= 0) { 
          if (res == SOCKET_ERROR) { 
           // error handling ... 
          } else { 
           // client disconnected 
          } 
          break; 
         } 
        } 
    
        // callback happens here ... 
        callback(&head, payload.data()); 
    
        payload.clear(); 
    } 
    while (true); 
    
    closesocket(*client); 
    
    ... 
    
    closesocket(m_sock); 
    WSACleanup(); 
    

    クライアント:

    #pragma pack(push, 1) // or your compiler's equivalent 
    struct msgHeader { 
        short id; 
        short command; 
        short payloadLength; 
    }; 
    #pragma pack(pop) 
    
    bool sendAll(SOCKET s, const void *buf, int buflen) 
    { 
        const char *pbuf = (const char*) buf; 
    
        while (buflen > 0) { 
         int res = send(s, pbuf, buflen, 0); 
         if (res == SOCKET_ERROR) { 
          return false; 
         } 
         pbuf += res; 
         len -= res; 
        } 
    
        return true; 
    } 
    
    ... 
    
    WSADATA wsa; 
    int res = WSAStartup(MAKEWORD(2, 2), &wsa); 
    if (res != 0) { 
        // error handling... 
    } 
    
    m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    if (m_sock == INVALID_SOCKET) { 
        // error handling... 
    } 
    
    m_serverConfig.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    m_serverConfig.sin_family = AF_INET; 
    m_serverConfig.sin_port = htons("8080");  
    
    if (connect(m_sock, (struct sockaddr *)&m_serverConfig, sizeof(m_serverConfig)) == SOCKET_ERROR) { 
        // error handling... 
    } 
    
    std::string msg = "Hello from client"; 
    const msgHeader head{ htons(23), 0, htons(msg.size()) }; 
    
    do { 
        if (!sendAll(m_sock, &head, sizeof(head))) { 
         // error handling... 
         break; 
        } 
    
        if (!sendAll(m_sock, msg.c_str(), msg.size())) { 
         // error handling... 
         break; 
        } 
    } 
    while (true); 
    
    closesocket(m_sock); 
    WSACleanup(); 
    
    +0

    提案のためのThx。エラー処理私は投稿をきれいに保つために投稿しませんでした。最も顕著な違いは、ヘッダーを送信するときに 'htons()'を追加することです。送信時にはheaderSizeとdataSizeに+ 1のオフセットが必要であり、8個のメッセージが失敗した後も、これはまだ仕事をしません。私はポストにコード全体へのリンクを追加しました。 – Stanislav

    +0

    @Stanislav:マルチバイト整数は、プラットフォーム間の一貫性を保つために常にネットワークバイトオーダーで送信する必要があります。私が作ったもう一つの変更は、ヘッダーをコンパイラによって追加された可能なパディングを削除するためにバイトで整列させることでした。 –

    +0

    @Stanislav:Gitlabのサーバーコードを簡単に見ました。それはロジックエラー、メモリリーク、および一般的な悪い習慣でいっぱいです、それは恐ろしいコードです、私はそれに触れる気にしません。それは捨てて書き直す必要があります。私はクライアントコードを見ていませんでした。 –

    関連する問題