You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost::ASIO Windows下二次调用io_context::run报系统错误995求助

Boost ASIO 1.66.0 on Windows: Fixing Error 995 When Restarting io_context

Let's tackle your problem head-on. First, let's clarify whether this is expected behavior, then walk through concrete fixes to make your loop clean and error-free.

Is this expected behavior?

Yes, this is a side effect of how Windows' IO Completion Port (IOCP) works with Boost ASIO. When you close the socket while there are pending async_receive_from operations, Windows terminates those in-flight I/O requests with error 995 ("The I/O operation has been aborted...").

ASIO doesn't discard these completed (failed) operations immediately—they stay in the io_context's queue. When you call io_context::restart() and run() again, these leftover error notifications get dispatched to your callback, which is why you see the error in the second iteration. The repeated errors you mention happen because each loop iteration adds new pending operations that get left behind when you close the socket, leading to a backlog of error events that get processed on subsequent runs.

How to fix this gracefully?

The key is to properly cancel pending socket operations before stopping the io_context, and handle the cancellation cleanly in your callback. Here's how to adjust your code:

1. Add a method to cancel pending socket operations

First, update your receiver class with a cancel method to terminate all in-flight async operations on the socket:

void cancel()
{
  if (socket_.is_open())
  {
    boost::system::error_code ec;
    socket_.cancel(ec);
    // It's safe to ignore cancel errors here—socket might already be in a closing state
  }
}

2. Fix the shutdown sequence in main()

Your current shutdown order (stop io_context → close socket) is backwards. Instead, cancel operations first, then stop the io_context, wait for it to finish, then close the socket. This ensures all pending operations are properly cleaned up before closing the socket:

for (int i = 5; i > 0; --i)
{
  io_context.restart();
  r.open(
      boost::asio::ip::make_address(listen_address),
      boost::asio::ip::make_address(multicast_address));
  fut = std::async(std::launch::async, [&](){ io_context.run(); });
  std::this_thread::sleep_for(3s);
  
  // Graceful shutdown steps (in order!)
  r.cancel();          // Cancel all pending async socket operations
  io_context.stop();   // Tell io_context to stop accepting new work
  fut.get();           // Wait for the io_context thread to finish processing all events
  r.close();           // Now safely close the socket
}

3. Handle cancellation errors properly in the callback

In your do_receive callback, skip logging the operation_aborted error (since it's an expected result of our intentional cancellation) and don't restart the receive loop when this error occurs:

void do_receive()
{
  socket_.async_receive_from(
      boost::asio::buffer(data_), sender_endpoint_,
      [this](boost::system::error_code ec, std::size_t length)
      {
        if (!ec)
        {
          std::cout.write(data_.data(), length);
          std::cout << std::endl;
          do_receive(); // Restart receive loop only on success
        }
        else
        {
          // Only log errors that aren't intentional cancellations
          if (ec != boost::asio::error::operation_aborted)
          {
            std::cout << "Unexpected error: " << ec.message() << std::endl;
          }
          // No need to restart do_receive here—we're shutting down
        }
      });
}

Why this works:

  • socket_.cancel() tells IOCP to terminate all pending operations for the socket, which triggers your callback with boost::asio::error::operation_aborted—a standard, expected error instead of the system-level 995.
  • By cancelling operations before stopping the io_context, you ensure there are no leftover error events in the queue, so subsequent calls to io_context::run() won't process stale errors.
  • Properly handling the cancellation error prevents unnecessary logging and stops the receive loop from trying to restart after shutdown.

内容的提问来源于stack exchange,提问作者wtom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:45:06