boost async_read 没有使用 ssl 读取所有数据

boost async_read not reading all data with ssl

我正在使用 boost asio 进行联网,我注意到当我切换到 ssl 时,我的联网代码不再工作。问题似乎是这里的这一行:

boost::asio::async_read(socket_, boost::asio::buffer(inbound_header_),
        boost::bind(&connection::handle_read_header<Handler>,
            this, boost::asio::placeholders::error,
            boost::make_tuple(handler)));

据我所知,在调用处理程序之前,这应该始终读取 inbound_header_.size() 字节。它大部分时间都有效。但有时它只会读取 0 个字节,并且仍然调用错误代码为 0 的处理程序。我做错了什么吗?我从 boost asio 序列化示例中复制了该代码,所以我认为这应该可行。

最小工作样本here 在 Ubuntu 16.04 上使用 boost 1.61.0 进行测试 connection::handle_read_header 中的断言几乎每次都在启动后的前几秒内命中。

async_read() will read until either the buffers are full or an error occurs. If the read handler is being invoked with neither of these conditions being satisfied, then one potential culprit is that undefined behavior has been invoked. In this particular case, the code violates the thread safety requirement for boost::asio::ssl:::stream:

Distinct objects: Safe.

Shared objects: Unsafe. The application must also ensure that all asynchronous operations are performed within the same implicit or explicit strand.

具体来说,写操作是在显式链中执行的(connection::strand_);但是,读取操作是在链外执行的。由于服务器有多个线程运行,程序无法确保所有操作都在同一链中执行。

void connection::async_write(...)
{
  ...
  // `strand_` protects `message_queue_` and guarantees that writes to
  // `socket_` are only occurring within the strand.
  strand_.post([...]()
    {
      auto empty = message_queue_.empty();
      message_queue_.push([=]()
        {
          ...
          // Intermediate and final handlers are performed within `strand_`.
          boost::asio::async_write(socket_, ... strand_.wrap([...](...)
            {
              ...
              message_queue_.front()()
            }));
        });
      if (empty)
        message_queue_.front()();
    });
}

void connection::async_read(...)
{
  // Not performed within `strand_`, violating thread safety.
  boost::asio::async_read(socket_, ...,
    // Neither intermediate, final handler, nor subsequent read
    // operations are performed within `strand_`.
    [](...)
    {
      boost::asio::async_read(socket_, ...);
    }
  });
}

要解决此问题,请从 connection::strand_ 链中明确执行 async_read 操作。此外,操作的读取处理程序也应包含在 connection::strand_ 中:

void connection::async_read(...)
{
  strand_.post([...](...)
    {
      // async_read is now performed with the strand.
      boost::asio::async_read(socket_, ...,
        // Intermediate and final handlers are performed within the strand.
        strand_.wrap([](...)
          {
            boost::asio::async_read(socket_, ...);
          });
    });
}

有关如何保证所有异步操作和处理程序在一个链中执行的更多详细信息,请参阅 回答。