記事ディレクトリ
1. バッファのデータ構造について
ネットワーク ライブラリにはバッファというデータ構造があり、データの送受信時にデータをキャッシュする構造です。
boost::asio は2 つの構造体asio::mutable_bufferとasio::const_bufferを提供しており、これらは連続した空間であり、最初のバイトには後続のデータの長さが格納されます。
asio::mutable_buffer は書き込みサービスに使用され、asio::const_buffer は読み取りサービスに使用されます。ただし、どちらの構造もasioのAPIによって直接使用されることはありません。
apiのバッファパラメータとして、asio は複数のasio::mutable_bufferとasio::const_bufferで構成されるMutableBufferSequenceとConstBufferSequenceの概念を提案しました。つまり、boost::asio はスペースを節約するために、連続したスペースの一部を結合し、それをパラメータとしてAPIに渡します。
MutableBufferSequenceのデータ構造はstd::vector<asio::mutable_buffer>であることがわかります。
構造は次のとおりです。
各ベクトルにはmutable_bufferのアドレスが格納され、各mutable_bufferの最初のバイトはデータの長さを示し、その後にデータの内容が続きます。
このような複雑な構造はユーザーが使用するのには適していないため、asio は、複数形式のバイト ストリームを受け取り、 asio::mutable_buffers_1またはasio::const_buffers_1構造オブジェクトを返すbuffer()関数を提案しています。
buffer()に渡されたパラメータが読み取り専用型の場合、関数はasio::const_buffers_1型のオブジェクトを返します。
buffer()に渡された引数が書き込み可能な型の場合、型asio::mutable_buffers_1のオブジェクトが返されます。
asio::const_buffers_1およびasio::mutable_buffers_1 は、asio::mutable_bufferおよびasio::const_bufferのアダプターであり、MutableBufferSequenceおよびConstBufferSequenceの概念に準拠するインターフェースを提供するため、 boost::のAPI関数のパラメーターとして使用できます。アシオ。
1.1. 簡単に要約すると、buffer() 関数を使用して、使用するキャッシュ ストレージ データを生成できます。
たとえば、boostの送信インターフェイスsendに必要なパラメータのタイプはConstBufferSequenceです。
template<typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence & buffers);
「Hello World」タイプをこのタイプにキャストする必要があります。
void BoostAsio::UseConstBuffer(std::string& buffer) {
boost::asio::const_buffer asio_buff(buffer.c_str(), buffer.length());
std::vector<boost::asio::const_buffer> buffer_sequence;
buffer_sequence.emplace_back(asio_buff);
}
このコードは、C++プログラミング言語とBoost.Asioライブラリを使用して、ネットワークおよび非同期 I/O操作を処理します。コードの主な機能は、 std::stringオブジェクトからBoost.Asio const_bufferを作成し、このconst_bufferをconst_bufferオブジェクトのベクトルに追加することです。これは通常、ネットワーク データ送信にBoost.Asio を使用するときに行われます。
-
UseConstBuffer関数: UseConstBuffer関数は、入力として参照パラメーターバッファーを受け取ります。この関数の目的は、const_buffer を作成し、それをconst_bufferオブジェクトのベクトルに追加することです。
-
const_bufferの作成: このコードは、 buffer.c_str() (C スタイルの文字列表現) とbuffer.length() (文字列の長さ) をパラメータとして使用して、asio_buffという名前のconst_bufferを作成します。const_buffer は、読み取り操作用の定数データのバッファーを表します。
-
バッファー シーケンスの準備: boost::asio::const_bufferのインスタンスを保存するために、buffer_sequenceという名前のstd::vectorを作成します。emplace_back関数は、asio_buffをbuffer_sequenceに追加します。
-
これまでのところ、buffer_sequence には入力 std::string を表す const_bufferが含まれます。
-
ただし、考慮すべき重要な側面が 1 つあります。それは、buffer_sequenceスコープです。現在、buffer_sequence はUseConstBuffer関数内で定義されたローカル変数です。関数の外でbuffer_sequenceを使用したい場合は、戻り値または参照パラメータで渡す必要があります。
-
また、 Boost.Asio関数を使用してネットワーク ソケット経由でデータを送信するなど、実際のネットワーク操作のコードの他の部分でbuffer_sequenceが使用されていることを確認してください。
-
最後に、bufferパラメーターによって参照されるstd::stringオブジェクトの有効期間を適切に処理することを忘れないでください。const_buffer は文字列の基礎となる文字データを参照するため、文字列はバッファの使用期間中有効であることが保証されます。
emplace_back() と Push_back() の違い:
コードでは、emplace_back() 関数を使用して asio_buff をbuffer_sequence に追加します。emplace_back() は、C++ 標準ライブラリの std::vector の関数であり、コンテナの最後に新しい要素を構築するために使用されます。
対照的に、push_back()関数はstd::vectorに要素を追加する別の関数ですが、構築された要素 (オブジェクト) を渡す必要があります。つまり、Push_back()を使用する場合は、最初にconst_bufferオブジェクトを作成してから、それを関数に渡す必要があります。また、emplace_back()を使用すると、事前にオブジェクトを作成しなくても、コンテナ内で新しい要素を直接構築できます。
一般に、emplace_back() は追加のオブジェクト構築やコピー操作を回避できるため、push_back()よりも効率的です。コード内でemplace_back()を使用して asio_buff を追加することは、 const_bufferオブジェクトをコンテナ内で直接構築できるため、良い選択です。
-
オブジェクトの構築時間:
- Push_back() は、すでに構築されているオブジェクトを取得し、そのコピーをコンテナーに追加します。これは、push_back() を呼び出す前にオブジェクトを構築する必要があり、コンテナに追加するときにコピー構築または移動構築を実行する必要があることを意味します。
- emplace_back() はコンテナ内にオブジェクトを直接構築し、最初に構築してからコピーまたは移動する手順を回避します。渡されたパラメータはオブジェクトの構築に直接使用されます。
-
コピーと移動の操作:
- Push_back() を使用する場合、追加されたオブジェクトがすでに構築されている場合は、コピー構築 (コンテナ内のオブジェクト タイプがコピー構築をサポートしている場合) または移動構築 (移動構築をサポートしている場合) 操作を実行して、オブジェクトのコピーを追加する必要があります。オブジェクトをコンテナに入れます。
- emplace_back() を使用する場合、オブジェクトはコンテナ内に直接構築され、コピーや移動操作が回避されます。
-
メモリ割り当て:
- Push_back() を使用してオブジェクトが追加されると、最初にオブジェクトのコピーを保存するためにメモリが割り当てられ、次にコピーまたは移動操作が実行され、最後にコンテナのサイズが変更されます。これには、複数のメモリの割り当てと割り当て解除が含まれる場合があります。
- emplace_back() を使用すると、コンテナ内にオブジェクトが直接構築され、追加のメモリ割り当てと割り当て解除が回避されます。
- 要約すると、emplace_back() は通常、オブジェクトをコンテナ内に直接構築し、余分なコピーや移動操作、不必要なメモリの割り当てや割り当て解除を回避するため、より効率的です。これにより、構成オブジェクトをコンテナに追加する必要がある場合に emplace_back() が優れています。
1.2 ではありますが、これは複雑すぎるため、バッファ関数を直接使用して、send で必要なパラメータの型に変換できます。
void BoostAsio::UseConstBuffer1(std::string& buffer) {
boost::asio::const_buffers_1 out_buffer(boost::asio::buffer(buffer));
}
Boost.Asioライブラリは、 const_buffers_1オブジェクトを作成するためにコード セグメントで使用され、std::string はboost::asio::buffer(buffer)を介してネットワーク送信用のバッファに変換されます。このコードについて説明しましょう:
-
関数名とパラメータ:
- この関数の名前はUseConstBuffer1で、タイプstd::string&のパラメーターバッファーを受け入れ、受信文字列が送信されるデータです。
-
const_buffers_1オブジェクトを作成します。
- const_buffers_1は、非同期 I/O 操作用の定数バッファをラップするBoost.Asioライブラリのクラスです。
- boost::asio::buffer(buffer )を介して、バッファ(std::string) をBoost.Asioバッファ オブジェクトに変換します。
コードではconst_buffers_1オブジェクトを作成しますが、このオブジェクトは関数の終了後に破棄されるため、ソケットへのデータ送信など、実際のネットワーク操作では引き続きこのオブジェクトをコード内で使用する必要があります。
1.3. Output_buf は送信インターフェイスに直接渡すことができます。配列を send で受け入れられる型に変換することもできます。
void BoostAsio::UseBufferArray() {
const size_t buf_size = 30;
std::unique_ptr<char[]> buf(new char[buf_size]);
auto input_buf = boost::asio::buffer(static_cast<void*>(buf.get()), buf_size);
}
このコードは次のことを行います。
-
バッファ サイズを定義します。
- buf_sizeはバッファのサイズを示す定数で、ここでは 30 バイトに設定されています。
-
バッファーの配列を作成します。
- サイズbuf_sizeのchar配列は、 std::unique_ptrを使用して作成されます。
- std::unique_ptr<char[]> は、動的に割り当てられたchar配列を管理するためのスマート ポインターです。そうすることで、スコープが終了したときにメモリが自動的に解放されます。
-
入力バッファを作成します。
- 入力バッファはboost::asio::buffer関数を使用して作成されます。
- バッファ関数の最初のパラメータはvoid * ポインタで、データの開始アドレスを指します。ここでは、buf.get()を使用して、 std::unique_ptrによって管理される生のポインタを取得します。
- 2 番目のパラメーターはバッファーのサイズbuf_sizeです。
要約すると、このコードの目的は、 std::unique_ptrを使用して動的に割り当てられたメモリを管理する、サイズ30バイトの入力バッファを作成することです。このバッファは、このバッファへのデータの非同期読み取りなどの非同期I/O操作で使用できます。実際のアプリケーションでは、boost::asio::io_contextやソケットなどのコンポーネントを使用して、特定の非同期I/O操作を実装する必要があることに注意してください。
1.4. ストリーミング操作の場合、streambuf を使用して入力ストリームと出力ストリームを streambuf でバインドし、ストリーミング入力と出力を実現できます。
id use_stream_buffer() {
asio::streambuf buf;
std::ostream output(&buf);
// Writing the message to the stream-based buffer.
output << "Message1\nMessage2";
// Now we want to read all data from a streambuf
// until '\n' delimiter.
// Instantiate an input stream which uses our
// stream buffer.
std::istream input(&buf);
// We'll read data into this string.
std::string message1;
std::getline(input, message1);
// Now message1 string contains 'Message1'.
}