0

boost::asio のタイムアウトを使用してブロック API のかなり標準的なエミュレーションを実装しました。これが私の主なサイクルです:

io_service io_svc;
tcp::endpoint endpoint(tcp::v4(), m_port);
tcp::acceptor acceptor(io_svc, endpoint);

accept_helper acc_hlpr(acceptor, 5000);

while (m_bStop == false)
{
  tcp::socket socket(io_svc);

  if (acc_hlpr.accept(socket))
  {
    // do stuff

    socket.close();
  }
}


これはヘルパークラスです

class accept_helper
{
public:
  accept_helper (tcp::acceptor &acc, size_t msTO) : m_timer(acc.get_io_service()), m_acceptor(acc), m_msTO(msTO) { }

  bool accept (tcp::socket &socket)
  {
    m_bTimeout = false;
    m_bAccept = false;

    m_timer.expires_from_now(boost::posix_time::milliseconds(m_msTO));
    m_timer.async_wait(boost::bind(&accept_helper::handle_timeout, this, boost::asio::placeholders::error));

    m_acceptor.async_accept(socket, boost::bind(&accept_helper::handle_accept, this));  

    m_timer.get_io_service().run_one();
    m_timer.get_io_service().reset();

    if (m_bAccept)
    {
      m_timer.cancel();
      return true;
    }
    else if (m_bTimeout)
    {
      // BOOST_ASIO_ENABLE_CANCELIO is defined
      boost::system::error_code ec;
      m_acceptor.cancel(ec);
    }

    return false;
  }

private:
  void          handle_accept (void)
  {
    boost::mutex::scoped_lock lock(m_mutex);
    m_bAccept = true;
  }

  void          handle_timeout(const boost::system::error_code & error)
  {
    if (!error)
    {
      boost::mutex::scoped_lock lock(m_mutex);
      m_bTimeout = true;
    }
  }

private:
  boost::asio::deadline_timer m_timer;
  boost::asio::ip::tcp::acceptor &m_acceptor;
  boost::mutex m_mutex;
  size_t m_msTO;
  bool m_bTimeout;
  bool m_bAccept;
};

問題は、タイマーが最初の反復でのみ待機することです。他の場合、run_one メソッドはすぐに戻り、フラグは設定されません。タイマーをローカルにしようとしましたが、役に立ちませんでした。タイマーを毎回待機させる方法は?

修正版

bool accept (tcp::socket &socket)
  {
    m_bTimeout = false;
    m_bAccept = false;

    m_timer.expires_from_now(boost::posix_time::milliseconds(m_msTO));
    m_timer.async_wait(boost::bind(&accept_helper::handle_timeout, this, boost::asio::placeholders::error));

    m_acceptor.async_accept(socket, boost::bind(&accept_helper::handle_accept, this, boost::asio::placeholders::error));    

    m_timer.get_io_service().reset();
    m_timer.get_io_service().run_one();

    if (m_bAccept)
    {
      m_timer.cancel();
    }
    else if (m_bTimeout)
    {
      boost::system::error_code ec;
      m_acceptor.cancel(ec);
    }

    while (m_timer.get_io_service().run_one());

    return m_bAccept;
  }

private:
  void          handle_accept (const boost::system::error_code & error)
  {
    if (!error)
    {
      boost::mutex::scoped_lock lock(m_mutex);
      m_bAccept = true;
    }
  }

  void          handle_timeout(const boost::system::error_code & error)
  {
    if (!error)
    {
      boost::mutex::scoped_lock lock(m_mutex);
      m_bTimeout = true;
    }
  }
4

1 に答える 1

2

このio_service::reset()機能はio_service、停止状態からの実行の再開のみを許可します。に既にキューに入れられているハンドラは削除されませんio_service。この場合、io_service(async_waitと) で 2 つの操作が開始されますが、のイベント ループが によって処理されているasync_acceptため、実行されるハンドラは 1 つだけです。への次の呼び出し時に、前の呼び出しからのハンドラーが実行されます。io_serviceio_service::run_one()accept_helper::accept()

これを解決するには、io_service両方のハンドラーが呼び出されるまで実行することを検討してください。io_serviceこの回答とBoost.Asioブロッキングtcpクライアントタイムアウトの例に示されているように、一部のソリューションは最後まで実行されます。

于 2013-06-25T20:49:41.930 に答える