35

時々、boost::asio は私が望む前に、つまりサーバーが切断を適切に処理する前に切断するように見えます。クライアントはメッセージを完全に送信したと考えているように見えるため、これがどのように可能かはわかりませんが、サーバーがエラーを発したときにメッセージヘッダーを読み取っていません...テスト中、これはおそらく5回に1回しか発生しません。サーバーはクライアントのシャットダウン メッセージを受信し、クライアントを正常に切断します。

エラー: 「既存の接続がリモート ホストによって強制的に閉じられました」

クライアントの切断:

void disconnect()
{
    boost::system::error_code error;
    //just creates a simple buffer with a shutdown header
    boost::uint8_t *packet = createPacket(PC_SHUTDOWN,0);
    //sends it
    if(!sendBlocking(socket,packet,&error))
    {
        //didnt get here in my tests, so its not that the write failed...
        logWrite(LOG_ERROR,"server",
            std::string("Error sending shutdown message.\n")
            + boost::system::system_error(error).what());
    }

    //actaully disconnect
    socket.close();
    ioService.stop();
}
bool sendBlocking(boost::asio::ip::tcp::socket &socket,
    boost::uint8_t *data, boost::system::error_code* error)
{
    //get the length section from the message
    boost::uint16_t len = *(boost::uint16_t*)(data - 3);
    //send it
    asio::write(socket, asio::buffer(data-3,len+3),
        asio::transfer_all(), *error);
    deletePacket(data);
    return !(*error);
}

サーバー:

void Client::clientShutdown()
{
    //not getting here in problem cases
    disconnect();
}
void Client::packetHandler(boost::uint8_t type, boost::uint8_t *data,
    boost::uint16_t len, const boost::system::error_code& error)
{
    if(error)
    {
        //error handled here
        delete[] data;
        std::stringstream ss;
        ss << "Error recieving packet.\n";
        ss << logInfo() << "\n";
        ss << "Error: " << boost::system::system_error(error).what();
        logWrite(LOG_ERROR,"Client",ss.str());

        disconnect();
    }
    else
    {
        //call handlers based on type, most will then call startRead when
        //done to get the next packet. Note however, that clientShutdown
        //does not
        ...
    }
}



void startRead(boost::asio::ip::tcp::socket &socket, PacketHandler handler)
{
    boost::uint8_t *header = new boost::uint8_t[3];
    boost::asio::async_read(socket,boost::asio::buffer(header,3),
        boost::bind(&handleReadHeader,&socket,handler,header, 
        boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
}
void handleReadHeader(boost::asio::ip::tcp::socket *socket, PacketHandler handler,
    boost::uint8_t *header, size_t len, const boost::system::error_code& error)
{
    if(error)
    {
        //error "thrown" here, len always = 0 in problem cases...
        delete[] header;
        handler(0,0,0,error);
    }
    else
    {
        assert(len == 3);
        boost::uint16_t payLoadLen  = *((boost::uint16_t*)(header + 0));
        boost::uint8_t  type        = *((boost::uint8_t*) (header + 2));
        delete[] header;
        boost::uint8_t *payLoad = new boost::uint8_t[payLoadLen];

        boost::asio::async_read(*socket,boost::asio::buffer(payLoad,payLoadLen),
            boost::bind(&handleReadBody,socket,handler,
            type,payLoad,payLoadLen,
            boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
    }
}
void handleReadBody(ip::tcp::socket *socket, PacketHandler handler,
    boost::uint8_t type, boost::uint8_t *payLoad, boost::uint16_t len,
    size_t readLen, const boost::system::error_code& error)
{
    if(error)
    {
        delete[] payLoad;
        handler(0,0,0,error);
    }
    else
    {
        assert(len == readLen);
        handler(type,payLoad,len,error);
        //delete[] payLoad;
    }
}
4

5 に答える 5

34

socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec)への呼び出しの前に、そこに を呼び出す必要があると思いますsocket.close()

basic_stream_socket::closeのboost::asio ドキュメントには次のように記載されています。

接続されたソケットの適切なクローズに関する移植可能な動作については、ソケットをクローズする前に shutdown() を呼び出します。

これにより、ソケットで保留中の操作が適切にキャンセルされ、socket.close の呼び出し前にバッファーがフラッシュされるようになります。

于 2010-06-18T08:23:23.143 に答える
13

close()メソッドとshutdown()メソッドの両方でこれを実行しようとしました

socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec)

シャットダウン方法は、2つのうちで最も優れています。ただし、ASIOがすべてを処理するため、ASIOソケットのデストラクタを使用するのがクリーンな方法であることがわかりました。したがって、目標は、ソケットをスコープから外すことです。これで、shared_ptrを使用してこれを簡単に実行し、shared_ptrを新しいソケットまたはnullにリセットできます。これはASIOソケットのデストラクタを呼び出し、寿命は良好です。

于 2013-01-07T23:53:11.580 に答える
5

たぶん、これが起こっていることです:

  • クライアントが切断パケットを送信
  • クライアントはソケットをシャットダウンします
  • サーバー読み取りハンドラーが呼び出されますが、ソケットが既に閉じられているため、シャットダウン パケットに関連するエラーがあります。

エラーが発生した場合、読み取りハンドラーに表示されますが、シャットダウン パケットが存在するかどうかを確認することはありません。たぶんそうです。基本的に私が言っているのは、サーバーがそれらを個別に処理する前に、クライアントがクローズ パケットとシャットダウン パケットの両方を送信できる場合があるということです。

于 2010-01-03T01:13:22.087 に答える
4

async_write()を使用し、書き込みハンドラー内にsocket.close()を配置します。これにより、パケットがブーストasioによって処理され、処理の途中で無視されないようになります(close()呼び出しのため)。

于 2010-08-30T21:38:55.357 に答える
4

私は非常によく似た問題を抱えています。Windowsのリサイクル接続に関連していると思います。次はおなじみですか?

  • プログラムを開始するとすぐにこのエラーが発生しますが、接続が確立された後ではありませんか?
  • アプリケーションを再起動する前に 4 分以上待っても、エラーは発生しませんか?

tcp 仕様では、デフォルトで、tcp 接続が閉じられたときに最終確認応答を 4 分間待機する必要があると指定されています。これらの接続は、netstat を使用して FIN_WAIT 状態で確認できます。Windows OS は、まったく同じシステムに接続しようとすると検出し、これらの部分的に閉じられた接続を取得してリサイクルします。プログラムの 2 回目の呼び出しでは、最初の実行で残された「閉じられた」接続が取得されます。次の承認を取得してから、実際に閉じます。

于 2010-08-30T21:48:03.047 に答える