使用ssl来提升async_read不读取所有数据

时间:2016-12-22 10:11:00

标签: c++ ssl boost boost-asio

我使用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进行测试 断言连接:: handle_read_header几乎每次都会在发布后的前几秒内被命中。

1 个答案:

答案 0 :(得分:1)

async_read()将读取,直到缓冲区已满或发生错误。如果在没有满足这些条件的情况下调用读处理程序,那么一个潜在的罪魁祸首是已经调用了未定义的行为。在这种特殊情况下,代码违反了boost::asio::ssl:::stream

的线程安全要求
  

不同的对象:安全。

     

共享对象:不安全。应用程序还必须确保所有异步操作都在同一个隐式或显式链中执行。

具体地,写操作在显式链(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_, ...);
    }
  });
}

要解决此问题,请在async_read链中明确执行connection::strand_操作。此外,操作的读取处理程序也应该包含在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_, ...);
          });
    });
}

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