2

私のプログラムは常にプラットフォーム依存の同期受信を使用していました。これは、タイムアウトまたは受信イベントまで実行をブロックしました。

recv(buf, size, timeout);

ここで、このコードをブーストに置き換えてクロスプラットフォームにしたいと考えています。私は解決策を見つけましたが、それはかなり醜いと思います(単一の関数呼び出しと比較して)。私はこれを書きました:

void IPV4_HOST::recv_timer_handler(const boost::system::error_code & e)
{
    if (e.value() == boost::system::errc::success) {
        f_recv_timeout = true;
        asio_socket.cancel();
    }
}

void IPV4_HOST::recv_handler(const boost::system::error_code & , size_t bytes)
{
    recv_timer.cancel();
    bytes_received = bytes;
}

int IPV4_HOST::receive(void * buf, size_t buf_size, TIME::INTERVAL timeout)
{
    f_recv_timeout = false;
    bytes_received = 0;

    recv_timer.expires_from_now(timeout.get_boost_milli());
    recv_timer.async_wait(boost::bind(&IPV4_HOST::recv_timer_handler, this, boost::asio::placeholders::error));

    asio_socket.async_read_some(boost::asio::buffer(buf, buf_size),
                                boost::bind(&IPV4_HOST::recv_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));

    io_service.run();

    if (f_recv_timeout)
        throw IO::TimeoutReceiveException();

    return bytes_received;
}

私はそれを正しく行っていますか?これを行う簡単な方法はありますか?

4

1 に答える 1

5

これは正しい方向ですが、考慮すべき微妙な点がいくつかあります。

  • に他の作品が投稿された場​​合io_service、 はIPV4_HOST::receive()その作品の処理を開始します。
  • io_service::run()が通常の状態に戻った場合は、 が停止したことを意味しますio_service。、、、またはへrun()の後続の呼び出しは、io_service が でない限り、すぐに戻ります。run_one()poll()poll_one()reset()
  • 両方の非同期操作が同時に完了し、両方の完了ハンドラーが正常に実行できる状態になる可能性があります。この動作は、のコメント セクションで強調されていdeadline_timer::cancel()ます。ただし、すべての非同期操作はこの動作を示します。既存のコードでは、が 0 より大きいIO::TimeoutReceiveException場合にスローされる可能性があります。bytes_received

詳細を処理する 1 つのソリューションio_serviceと、完了ハンドラーを使用した非決定論的な実行順序は、次のようになります。

void IPV4_HOST::recv_timer_handler(const boost::system::error_code & e)
{
  timer_handled = true;
  if (!e) {
    f_recv_timeout = true;
    asio_socket.cancel();
  }
}

void IPV4_HOST::recv_handler(const boost::system::error_code &,
                             size_t bytes)
{
  recv_handled = true;
  recv_timer.cancel();
  bytes_received = bytes;
}

int IPV4_HOST::receive(void * buf, size_t buf_size, TIME::INTERVAL timeout)
{
  timer_handled  = false;
  recv_handled   = false;
  f_recv_timeout = false;
  bytes_received = 0;

  recv_timer.expires_from_now(timeout.get_boost_milli());
  recv_timer.async_wait(
    boost::bind(&IPV4_HOST::recv_timer_handler, this,
                boost::asio::placeholders::error));

  asio_socket.async_read_some(
    boost::asio::buffer(buf, buf_size),
    boost::bind(&IPV4_HOST::recv_handler, this, 
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));

  // If a handler has not ran, then keep processing work on the io_service.
  // We need to consume both handlers so that old handlers are not in the
  // io_service the next time receive is called.
  while (!timer_handled || !recv_handled)
  {
    io_service.run_one();
  }

  // If the io_service has stopped (due to running out of work), then reset
  // it so that it can be run on next call to receive.
  if (io_service.stopped())
    io_service.reset();

  // If no bytes were received and the timeout occurred, then throw.  This
  // handles the case where both a timeout and receive occurred at the 
  // same time.
  if (!bytes_received && f_recv_timeout)
    throw IO::TimeoutReceiveException();

  return bytes_received;
}

また、クロスプラットフォームの動作を得ようとしているので、 の発言を読んでくださいbasic_stream_socket::cancel()。注意すべきプラットフォーム固有の動作がいくつかあります。

于 2013-03-01T13:55:32.250 に答える