Cancel async_read due to timeout

后端 未结 1 430
小鲜肉
小鲜肉 2020-12-06 03:28

I\'m trying to write a wrapper synchronous method around async_read to allow non blocking reads on a socket. Following several examples around internet I have d

相关标签:
1条回答
  • 2020-12-06 04:12

    The main difference between io_service::run_one() and io_service::poll_one() is that run_one() will block until a handler is ready to run, whereas poll_one() will not wait for any outstanding handlers to become ready.

    Assuming the only outstanding handlers on _io_service are handle_timeout() and handle_read(), then run_one() does not require a loop because it will only return once either handle_timeout() or handle_read() have ran. On the other hand, poll_one() requires a loop because poll_one() will return immediately, as neither handle_timeout() nor handle_read() are ready to run, causing the function to eventually return.

    The main issue with the original code, as well as the fix proposal #1, is that there are still outstanding handlers in the io_service when async_read_helper() returns. Upon the next call to async_read_helper(), the next handler to be invoked will be a handler from the previous call. The io_service::reset() method only allows the io_service to resume running from a stopped state, it does not remove any handlers already queued into the io_service. To account for this behavior, try using a loop to consume all of the handlers from the io_service. Once all handlers have been consumed, exit the loop and reset the io_service:

    // Consume all handlers.
    while (_io_service->run_one())
    {
      if (_message_received)
      {
        // Message received, so cancel the timer.  This will force the completion of
        // handle_timer, with boost::asio::error::operation_aborted as the error.
        timer.cancel();
      }
      else if (_timeout_triggered)
      {
        // Timeout occured, so cancel the socket.  This will force the completion of
        // handle_read, with boost::asio::error::operation_aborted as the error.
        _socket->cancel();
      }
    }
    
    // Reset service, guaranteeing it is in a good state for subsequent runs.
    _io_service->reset();
    

    From the caller's perspective, this form of timeout is synchronous as run_one() blocks. However, work is still being made within the I/O service. An alternative is to use Boost.Asio's support for C++ futures to wait on a future and perform a timeout. This code can be easier to read, but it requires at least one other thread to be processing the I/O service, as the thread waiting on the timeout is no longer processing the I/O service:

    // Use an asynchronous operation so that it can be cancelled on timeout.
    std::future<std::size_t> read_result = boost::asio::async_read(
        socket, buffer, boost::asio::use_future);
    
    // If timeout occurs, then cancel the operation.
    if (read_result.wait_for(std::chrono::seconds(1)) == 
        std::future_status::timeout)
    {
      socket.cancel();
    }
    // Otherwise, the operation completed (with success or error).
    else
    {
      // If the operation failed, then on_read.get() will throw a
      // boost::system::system_error.
      auto bytes_transferred = read_result.get();
      // process buffer
    }
    
    0 讨论(0)
提交回复
热议问题