0

反対票を投じる方への注意: この質問は、asio の非同期の側面に関するものではありません (ただし、非同期ソリューションはここでは意味があるかもしれませんが、これは最後に提示する質問です)。これは、実際には asio tcp ソケット ラッパーでの streambuf と ostreams の使用に関するものです。例/チュートリアルでは、この特定の側面 (書き込み呼び出しの細分化) については説明していません。

何らかのイベントに応答して外部サーバーにかなり大きなデータ (~2MB) を送信する必要があるプラグイン環境用の (できれば単純な) コードを書いています。データはかなり迅速かつ完全に送信する必要がありますが、頻度は低く、生のパフォーマンスについてはあまり心配していません。Google の Protocol Buffers を使用してデータをシリアル化しています。

現在のところ、ほとんど機能する次のコードがあります。

#include <boost/asio.hpp>

// connect to the server:
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(server_address, server_port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket(io_service);
boost::asio::connect(socket, endpoint_iterator);

// float_array consists of ~500,000 floats in a ProtoBuf message:
//
// message FloatArray {
//   repeated float data = 1 [packed=true];
// }

// send the serialized float_array to the server:
boost::asio::streambuf b;
std::ostream os(&b);
float_array.SerializeToOstream(&os);
boost::asio::write(socket, b);

// the TCP connection *must* now close to signal the server

これに関する問題は、私が作業している環境 (マルチスレッド) では、write()操作に時間がかかりすぎる (ブロックされる) と見なされ、スレッドが終了することです。独自のスレッドを作成することは許可されていないため、write() 操作をいくつかの個別の書き込みに分割する必要があります。

私の懸念は、これをどのように行うかです。これを使用して正確なバイト数を送信できることはわかっています。

boost::asio::write(socket, b, boost::asio::transfer_exactly(65536));

しかし、これを正しく行うには、ostream に残っているバイト数を正確に知る必要があります。b.size()これを使用できるように、それに応じて減少することに気付きました。ただし、書き込みを分割するには、この関数の呼び出しの間に状態を保存する必要があります。

私が持っている 1 つのオプションは、書き込み関数の呼び出しの間に streambuf b と ostream os の両方を保存することですが、これを行うためのより良い方法があるかどうか疑問に思っています。私が知る限り、ProtoBuf 出力を部分的にシリアル化することはできないので、float_array.SerializeToOstream(). 問題は、利用可能なバイト数を ostream に直接問い合わせる適切な方法があるかどうか、または他のメカニズムを利用する適切な方法があるかどうかです (boost::asio::bufferおそらく?)。

boost::asio のドキュメントを自分で確認できてうれしいです。作業を進めなければならないドキュメントがたくさんあり、パズルのどの部分がわからないので、続行する方法について少しガイダンスを探しているだけです。関連性があります。

考え-boost::asioを使用して、この種の状態を処理できるこれらの行に沿って、ある種のシングルスレッド非同期「送信者」を作成することは可能ですか? たとえば、ある種のノンブロッキングwrite()関数を呼び出してから、完了をチェックしてから TCP 接続を閉じるある種のコールバック (または頻繁にアクセスされる関数) を使用できますか?

4

1 に答える 1

2

ストリームで「待機中」のデータの量を直接決定するために ostream を照会する方法を見つけることはできませんでしたが、ostream を完全に回避して、データをchar*配列にシリアル化することができました。boost::asio::write()これは、次の関数を使用して、古いスタイルの C ソケット メソッドと同様の方法で送信できます。

...
tcp::socket socket(io_service);
char * buffer = new char[size];  // or a smart-ptr
float_array.SerializeToArray(static_cast<void*>(buffer, size));
void * p = static_cast<void*>(buffer);
int bytes_sent = boost::asio::write(socket, boost::asio::buffer(p, bytes_to_send);

boost::asio::streambufまたは、との使用が優先される場合は、ostream を使用してデータを書き込んだ後で、std::ostream( を使用して) streambuf を照会できるようです。.size()

boost::asio::streambuf b;
std::ostream os(&b);
float_array.SerializeToOstream(&os);

// send a chunk of a particular size
int bytes_to_send = std::min(chunk_size, b.size());
cout << b.size() << endl;  // shows amount of remaining data
boost::asio::write(socket, b, boost::asio::transfer_exactly(bytes_to_send));
cout << b.size() << endl;  // shows a reduction in amount of remaining data

したがって、これが (チャンクごとに) 複数回呼び出される場合は、ostream、streambuf、および io_service をスコープ内に保持する必要があります。

于 2013-10-27T09:53:07.773 に答える