async_read
ソケットでの非ブロック読み取りを許可するラッパー同期メソッドを作成しようとしています。インターネットに関するいくつかの例に従って、ほぼ正しいと思われるソリューションを開発しましたが、機能していません。
このクラスは、次の関連する属性とメソッドを宣言します。
class communications_client
{
protected:
boost::shared_ptr<boost::asio::io_service> _io_service;
boost::shared_ptr<boost::asio::ip::tcp::socket> _socket;
boost::array<boost::uint8_t, 128> _data;
boost::mutex _mutex;
bool _timeout_triggered;
bool _message_received;
boost::system::error_code _error;
size_t _bytes_transferred;
void handle_read(const boost::system::error_code & error, size_t bytes_transferred);
void handle_timeout(const boost::system::error_code & error);
size_t async_read_helper(unsigned short bytes_to_transfer, const boost::posix_time::time_duration & timeout, boost::system::error_code & error);
...
}
メソッドはすべての複雑さをカプセル化するものでありasync_read_helper
、他の 2 つとは単なるイベント ハンドラーです。3 つのメソッドの実装を次に示します。handle_read
handle_timeout
void communications_client::handle_timeout(const boost::system::error_code & error)
{
if (!error)
{
_mutex.lock();
_timeout_triggered = true;
_error.assign(boost::system::errc::timed_out, boost::system::system_category());
_mutex.unlock();
}
}
void communications_client::handle_read(const boost::system::error_code & error, size_t bytes_transferred)
{
_mutex.lock();
_message_received = true;
_error = error;
_bytes_transferred = bytes_transferred;
_mutex.unlock();
}
size_t communications_client::async_read_helper(unsigned short bytes_to_transfer, const boost::posix_time::time_duration & timeout, boost::system::error_code & error)
{
_timeout_triggered = false;
_message_received = false;
boost::asio::deadline_timer timer(*_io_service);
timer.expires_from_now(timeout);
timer.async_wait(
boost::bind(
&communications_client::handle_timeout,
this,
boost::asio::placeholders::error));
boost::asio::async_read(
*_socket,
boost::asio::buffer(_data, 128),
boost::asio::transfer_exactly(bytes_to_transfer),
boost::bind(
&communications_client::handle_read,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
while (true)
{
_io_service->poll_one();
if (_message_received)
{
timer.cancel();
break;
}
else if (_timeout_triggered)
{
_socket->cancel();
break;
}
}
return _bytes_transferred;
}
私が持っている主な質問は、なぜこれがループをオンにしてループ_io_service->poll_one()
なしでループと呼び出しなしで機能するの_io_service->run_one()
ですか? また、Boost と Asio の操作に慣れている人にとっては正しいように見えるかどうかも知りたいです。ありがとうございました!
修正提案 #1
Jonathan Wakelyのコメントによると、ループは、操作が終了した後に_io_service->run_one()
への呼び出しを使用して置き換えることができます。_io_service->reset()
次のようになります。
_io_service->run_one();
if (_message_received)
{
timer.cancel();
}
else if (_timeout_triggered)
{
_socket->cancel();
}
_io_service->reset();
いくつかのテストの後、この種のソリューションだけでは機能しないことを確認しました。メソッドは、handle_timeout
エラー コード で継続的に呼び出されていますoperation_aborted
。これらの通話を停止するにはどうすればよいですか?
修正提案 #2
twsansburyによる回答は正確であり、確かなドキュメントに基づいています。その実装により、 内で次のコードが生成されますasync_read_helper
。
while (_io_service->run_one())
{
if (_message_received)
{
timer.cancel();
}
else if (_timeout_triggered)
{
_socket->cancel();
}
}
_io_service->reset();
handle_read
メソッドに次の変更を加えます。
void communications_client::handle_read(const boost::system::error_code & error, size_t bytes_transferred)
{
if (error != boost::asio::error::operation_aborted)
{
...
}
}
このソリューションは、テスト中に確実で正しいことが証明されました。