一般的なネットワークプログラミングにおける書き込みバッファについて

StackOverflow https://stackoverflow.com/questions/1237259

質問

boost.asioを使用してサーバーを作成しています。接続ごとに読み取りおよび書き込みバッファーがあり、非同期読み取り/書き込み機能を使用します(async_write_some / async_read_some).

読み取りバッファーと async_read_some, 、 何も問題ない。ただ呼び出すだけ async_read_some 読み取りバッファは読み取りハンドラーでのみ読み取り専用であるため、関数は問題ありません(通常は同じスレッド内を意味します)。

ただし、書き込みバッファは複数のスレッドからアクセスする必要があるため、変更のためにロックする必要があります。

最初の質問です!

書き込みバッファのLOCKを回避する方法はありますか?

私は独自のパケットをスタック バッファに書き込み、それを書き込みバッファにコピーします。それから、電話してください async_write_some パケットを送信する機能。このように、2つのパケットをシリアルに送信した場合、次の呼び出しをしても大丈夫ですか? async_write_some 2回機能しますか?

2番目の質問です!

ソケットプログラミングにおける非同期書き込みの一般的な方法は何ですか?

読んでくれてありがとう。

役に立ちましたか?

解決

答え #1:

ロックが実行可能なアプローチであることは正しいですが、これらすべてを行うためのより簡単な方法があります。Boost には、ASIO に と呼ばれる優れた小さな構造があります。 strand. 。ストランドを使用してラップされたコールバックは、どのスレッドがコールバックを実行したかに関係なく、シリアル化され、保証されます。基本的に、あらゆるロックを処理します。

これは、ライターを必要な数だけ持つことができ、それらがすべて 同じ ストランド (つまり、単一のストランドをすべてのライターで共有します) はシリアルに実行されます。注意すべき点の 1 つは、すべての書き込みを行うためにメモリ内の同じ実際のバッファを使用しようとしていないことを確認することです。たとえば、次のようなことは避けるべきです。

char buffer_to_write[256];  // shared among threads

/* ... in thread 1 ... */
memcpy(buffer_to_write, packet_1, std::min(sizeof(packet_1), sizeof(buffer_to_write)));
my_socket.async_write_some(boost::asio::buffer(buffer_to_write, sizeof(buffer_to_write)), &my_callback);

/* ... in thread 2 ... */
memcpy(buffer_to_write, packet_2, std::min(sizeof(packet_2), sizeof(buffer_to_write)));
my_socket.async_write_some(boost::asio::buffer(buffer_to_write, sizeof(buffer_to_write)), &my_callback);

そこでは、実際の書き込みバッファを共有しています(buffer_to_write)。代わりに次のようなことをした場合は、問題ありません。

/* A utility class that you can use */
class PacketWriter
{
private:
  typedef std::vector<char>  buffer_type;

  static void WriteIsComplete(boost::shared_ptr<buffer_type> op_buffer, const boost::system::error_code& error, std::size_t bytes_transferred)
  {
    // Handle your write completion here
  }

public:
  template<class IO>
  static bool WritePacket(const std::vector<char>& packet_data, IO& asio_object)
  {
    boost::shared_ptr<buffer_type> op_buffer(new buffer_type(packet_data));

    if (!op_buffer)
    {
      return (false);
    }

    asio_object.async_write_some(boost::asio::buffer(*op_buffer), boost::bind(&PacketWriter::WriteIsComplete, op_buffer, boost::asio::placeholder::error, boost::asio::placeholder::bytes_transferred));
  }
};

/* ... in thread 1 ... */
PacketWriter::WritePacket(packet_1, my_socket);

/* ... in thread 2 ... */
PacketWriter::WritePacket(packet_2, my_socket);

ここで、ストランドも WritePacket に渡すと役立ちます。ただし、その考えはわかります。

答え #2:

すでに非常に良いアプローチをとっていると思います。私が提案したい 1 つの提案は、 async_write の代わりに async_write_some そのため、コールバックが呼び出される前にバッファ全体が書き込まれることが保証されます。

他のヒント

申し訳ありませんが、選択肢は 2 つあります:

  1. ロックを使用して書き込みステートメントをシリアル化するか、キューからリクエストを読み取る別のライタースレッドをより適切に開始すると、他のスレッドは、あまり競合することなくキューにリクエストを積み重ねることができます(いくつかのミューテックスが必要です)。

  2. 各書き込みスレッドに独自のソケットを与えてください。これは、ワイヤーの反対側のプログラムがサポートできる場合、実際にはより良いソリューションです。

変更をキューに入れて、書き込みハンドラー内のデータに対して実行できます。

ネットワークはおそらくパイプの最も遅い部分になるでしょう(変更が高価ではないと仮定すると)。そのため、ソケット層が以前のデータを送信している間にMODを実行できます。

頻繁に接続/切断している多数のクライアントを処理している場合にかかわらず、IO完了ポートまたは同様のメカニズムをご覧ください。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top