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

Posted

技术标签:

【中文标题】提升 async_read 不使用 ssl 读取所有数据【英文标题】:boost async_read not reading all data with ssl 【发布时间】:2016-12-22 10:11:00 【问题描述】:

我正在使用 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 测试 Assert in connection::handle_read_header 几乎每次都在启动后的前几秒内被命中。

【问题讨论】:

可以提供minimal reproducible example吗?可能有多种原因(例如,inbound_header_.size()0;未能满足 socket_ 的线程安全要求;低级驱动程序违反 API 合同等)。 在起始帖子中添加了一个 对于后代,minimal reproducible example 应该是最小的,通常是从头开始创建的,并且包含在原始问题中。外部链接及其可能包含的关键信息可能会消失。 【参考方案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_, ...);
    
  );

要解决此问题,请从 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_, ...);
          );
    );

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

【讨论】:

谢谢!我只是假设我必须确保没有 2 个并发读取/写入,并且不允许读取和写入同时发生

以上是关于提升 async_read 不使用 ssl 读取所有数据的主要内容,如果未能解决你的问题,请参考以下文章

提升 asio 取消读取而不取消写入

boost::asio::async_read 不回调我的处理函数

最大大小的 async_read()

野兽 async_read() 是如何工作的?有啥选择吗?

如何在到达终止字符时返回 boost::asio::async_read

优雅地取消 boost::asio::async_read