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();
}
簡単に試した限りでは動いてくれた。本当はもっと叩いて安全性などを確認したいところなのだけれど、最近、どうにも時間が無い。。今日はこれまで。