0

boost::asio を使用して C++ で記述されたソケット サーバーがあり、クライアントにデータを送信しています。

サーバーはデータをチャンクで送信し、クライアントは受信時に各チャンクを解析します。現在、どちらもほぼシングルスレッドです。

サーバーができるだけ速くデータを書き出すだけで、クライアントがデータを解析するのを待たないようにするには、サーバーでどのような設計を使用すればよいですか? サーバー上で非同期に何かをする必要があると思います。

これを達成するためにクライアントにも変更を加えることができると思いますが、理想的には、クライアントの記述方法に関係なく、サーバーはクライアントを待機するべきではありません。

次のようにソケットにデータを書き込んでいます。

size_t bytesWritten = m_Socket.Write( boost::asio::buffer(buffer, bufferSize));

アップデート:

Boost のメカニズムを使用して、ソケットに非同期で書き込みを試みます。http://www.boost.org/doc/libs/1_36_0/doc/html/boost_asio/tutorial/tutdaytime3/src.htmlを参照してください。

例えば

 boost::asio::async_write(socket_, boost::asio::buffer(message_),
        boost::bind(&tcp_connection::handle_write, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  • アレックス
4

5 に答える 5

1

ステファンの投稿へのコメントの続き:

クライアント側またはサーバー側のいずれかでバッファリングすることは間違いなく可能です。しかし、ニールが書いたことを必ず考慮してください。やみくもにデータのバッファリングを開始し、処理が送信に追いつかない場合は、おそらく望ましくない方法でバッファが増加します。

最近、パイプがクライアントであるかサーバーであるかを外部ユーザーが知らない/気にしない、単一のクライアント/サーバー、サーバー/クライアント間の接続として機能することを目的とした単純な「NetworkPipe」を実装しました。あなたが求めているものと同様のバッファリング状況を実装しましたが、どうですか? クラスはスレッド化されていました。これは、データをきれいにバッファリングするために私が理解できる唯一の方法でした。私が従った基本的なプロセスは次のとおりです。パイプに最大サイズを設定していることに注意してください。

  1. プロセス 1 はパイプを開始し、デフォルトはサーバーです。現在、内部スレッドはクライアントを待機しています。
  2. プロセス 2 は、既にサーバーであるパイプを開始し、デフォルトはクライアントです。
  3. これで接続が完了しました。最初に行うことは、最大バッファ サイズを交換することです。
  4. プロセス 1 はデータを書き込みます (もう一方の端には空のバッファーがあることに注意してください [#3 を参照])。
  5. プロセス 2 の内部スレッド (ソケットの select() を待機中) は、データが送信されたことを確認して読み取り、バッファリングします。プロセス 2 は、新しいバッファー サイズを P1 に送り返します。

これは非常に単純化されたバージョンですが、基本的にスレッド化することで、ブロックする select 呼び出しをいつでも待機できます。データが到着するとすぐに、それを読み取ってバッファリングし、新しいバッファ サイズを送り返します。同様のことを行って、データをやみくもにバッファリングすることもできます。バッファサイズを交換する必要がないため、実際にはかなり簡単ですが、おそらく悪い考えです。したがって、上記の例では、外部ユーザーがスレッドをブロックせずにデータの読み取り/書き込みを行うことができました (相手側のバッファーがいっぱいでない限り)。

于 2009-07-16T00:12:57.920 に答える
1

ソケットにデータを渡すとき、ソケットは受信者がそれを処理するのを待ちません。データが送信されるのを待ちません。データは、バックグラウンドで OS によって処理されるアウトバウンド キューに入れられます。書き込み関数は、実際に送信されたバイト数ではなく、送信のためにキューに入れられたバイト数を返します。

于 2009-07-15T23:45:45.867 に答える
1

TCP ではなく UDP を介してデータを転送することにより、非同期通信を確保できます。ただし、TCP を使用する必要がある場合は、クライアントがデータをすばやく保存し、別のスレッドで処理するか、cron ジョブを使用して非同期に処理します。

于 2009-07-15T23:38:44.257 に答える
1

ソケットを非ブロッキングに設定すると、書き込みがブロックされた場合に書き込みが失敗するはずです。その後、好きなようにデータをキューに入れ、後で別の試みが行われるように手配することができます。ブースト ソケット API でソケット オプションを設定する方法はわかりませんが、それはあなたが探しているものです。

しかし、これはおそらく価値があるよりも面倒です。おそらく同時に開いているいくつかのソケットから、書き込みの準備ができているソケットを選択し、いっぱいになるまでさらにデータを押し込み、繰り返す必要があります。ブースト ソケット API に に相当するものがあるかどうかはわかりません。selectそのため、複数のソケットのいずれかが書き込み可能になるまで一度に待機できます。

通常、サーバーがクライアント接続ごとにスレッドを開始する (またはプロセスを生成する) 理由は、サーバーが I/O を待機している間、他のクライアントにサービスを提供しながら、独自のキューの実装を回避できるようにするためです。「後で別の試行を手配する」最も簡単な方法は、専用スレッドでブロッキング I/O を行うことです。

ブーストがそのソケット API で異常なことを行っていない限り、できないことは、OS またはソケット ライブラリがブロックせずに任意の量のデータをキューに入れることを要求することです。データが書き込まれたときにコールバックする非同期 API がある場合があります。

于 2009-07-16T00:08:17.380 に答える
0

boost::asio::async_write メソッドを使用してソリューションを実装しました。

基本的:

  • クライアントごとに 1 つのスレッドがあります (私のスレッドは CPU バウンドの作業を行っています)
  • 各スレッドがある程度のデータを蓄積すると、async_write を使用してそれをソケットに書き込みます。以前の書き込みが完了しているかどうかは気にしません。
  • すべてのデータが書き出される前に CPU 処理が終了するため、コードはソケットの有効期間と書き出されるデータ バッファーを慎重に管理します。

これは私にとってはうまくいきます。これにより、サーバー スレッドは、CPU の処理が完了するとすぐに終了できます。

クライアントがすべてのデータを受信して​​解析するための全体的な時間は短縮されました。同様に、サーバーが各クライアントに費やす時間 (ウォール タイムの時計) がダウンします。

コードスニペット:

void SocketStream::Write(const char* data, unsigned int dataLength)
{
    // Make a copy of the data
    // we'll delete it when we get called back via HandleWrite
    char* dataCopy = new char[dataLength];
    memcpy( dataCopy,  data, dataLength );

    boost::asio::async_write
        (
        *m_pSocket,
        boost::asio::buffer(dataCopy, dataLength),
        boost::bind
            (
            &SocketStream::HandleWrite,                     // the address of the method to callback when the write is done
            shared_from_this(),                             // a pointer to this, using shared_from_this to keep us alive
            dataCopy,                                       // first parameter to the HandleWrite method
            boost::asio::placeholders::error,               // placeholder so that async_write can pass us values
            boost::asio::placeholders::bytes_transferred
            )
        );
}

void SocketStream::HandleWrite(const char* data, const boost::system::error_code& error, size_t bytes_transferred)
{
    // Deallocate the buffer now that its been written out
    delete data;

    if ( !error )
    {
        m_BytesWritten += bytes_transferred;
    }
    else
    {
        cout << "SocketStream::HandleWrite received error: " << error.message().c_str() << endl;
    }
}
于 2009-07-18T18:55:11.010 に答える