Boost.Asio 非同期処理のタイムアウトを設定する

タイムアウトには、async系メソッドと別にdeadline_timerを動かし、タイムアウトを設定します。


タイムアウトの前に非同期処理が完了した場合はdeadline_timer::cancel()を呼び、タイムアウト用のタイマーをキャンセルします。
そうすると、タイムアウトのハンドラが呼ばれなくなる・・・のではなく!タイムアウトのハンドラにエラー(boost::asio::error::operation_aborted)が渡されるので、タイムアウトハンドラがエラーじゃなかった場合に実際のタイムアウト処理を行います。


以下、サンプルコード:

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>

namespace asio = boost::asio;

class Client {
    asio::io_service& io_service_;
    asio::ip::tcp::socket socket_;
    asio::deadline_timer connect_timer_;
public:
    Client(asio::io_service& io_service)
        : io_service_(io_service),
          socket_(io_service),
          connect_timer_(io_service)
    {}

    void connect()
    {
        socket_.async_connect(
            asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.10"), 31400),
            boost::bind(&Client::on_connect, this, _1));

        // 5秒でタイムアウト
        connect_timer_.expires_from_now(boost::posix_time::seconds(5));
        connect_timer_.async_wait(boost::bind(&Client::on_connect_timeout, this, _1));
    }

    void on_connect(const boost::system::error_code& error)
    {
        if (error) {
            std::cout << "接続失敗: " << error.message() << std::endl;
        }
        else {
            // タイムアウトのタイマーを切る :
            // タイムアウトのハンドラにboost::asio::error::operation_abortedエラーが渡される
            connect_timer_.cancel();

            std::cout << "接続成功" << std::endl;
        }
    }

    void on_connect_timeout(const boost::system::error_code& error)
    {
        if (!error) {
            // タイムアウト : 接続を切る。接続のハンドラがエラーになる
            socket_.close();
        }
    }
};

int main()
{
    asio::io_service io_service;

    Client client(io_service);
    client.connect();

    io_service.run();
}