Boost::asioとspawnとタイムアウト
Boost::asio での非同期通信プログラムにおいて、spawn()
を使うスタイル(yield_context
を使ったコルーチンで)対話処理を記述しつつ deadline_timer
でタイムアウト制御してみたので備忘録。
要点は以下:
- 通常通り
boost::asio::deadline_timer
のasync_wait
を発行し、そのハンドラーでsocket
や 。acceptor
のcancel()
を呼ぶ - その後に、一定時間で打ち切りたい非同期処理を
yield_context
を使って呼び出す - その後に、その非同期処理が成功していたならばタイマーをキャンセルする
注意点は:
- タイマーのハンドラーに渡されるエラーコードは、非同期処理が指定時間内に成功したならば「エラー」(boost::system::errc::operation_canceled)、指定時間内に成功しなければ「成功」になる
- 非同期処理の成功後および(おそらく)タイムアウト=タイマーのハンドラーによるキャンセルではないエラーの検出時に、タイマーをキャンセルする
こんな感じで実装できる:
namespace asio = boost::asio; using std::placeholders::_1; const char *server_address = "127.0.0.1"; const u_short server_port = 9999; class MyClient { public: MyClient() : io_() , socket_(io_) {} int run() { boost::system::error_code ec; asio::spawn(io_, std::bind(&MyClient::talk, this, _1)); io_.run(ec); if (ec) { return EXIT_FAILURE; } return EXIT_SUCCESS; } void talk(asio::yield_context yield) { boost::system::error_code ec; auto remote_address = asio::ip::address::from_string(server_address); asio::ip::tcp::endpoint remote_endpoint(remote_address, server_port); asio::deadline_timer timer(io_); auto timeout_duration = boost::posix_time::seconds(4); timer.expires_from_now(timeout_duration); timer.async_wait([this](const boost::system::error_code& ec2) { if (!ec2) { std::cerr << "# Canceling socket operation.\n"; socket_.cancel(); } }); std::cout << "Connecting to " << remote_endpoint << "..." << "\n"; socket_.async_connect(remote_endpoint, yield[ec]); if (ec) { if (ec == boost::system::errc::operation_canceled) { std::cerr << "Canceled to connect; timed out. error=" << ec << "\n"; } else if (ec) { timer.cancel(); std::cerr << "Failed to connect. error=" << ec << "\n"; } return; } std::cout << "Successfully connected.\n"; timer.cancel(); std::cout << "Sleeping for 3 seconds...\n"; std::this_thread::sleep_for(std::chrono::seconds(3)); std::cout << "Done.\n"; socket_.close(); } private: asio::io_service io_; asio::ip::tcp::socket socket_; }; int main() { return MyClient().run(); }
簡単に試した限りでは動いてくれた。本当はもっと叩いて安全性などを確認したいところなのだけれど、最近、どうにも時間が無い。。今日はこれまで。