Boost.Asio中streambuf结合async_read_until与async_read_some的实现方案及内部原理咨询
Boost.Asio中streambuf结合async_read_until与async_read_some的实现方案及内部原理咨询
嗨,我来帮你搞定这个问题~先从代码实现说起,再给你拆解streambuf的内部逻辑,把你疑惑的点都讲清楚:
一、复用streambuf实现async_read_some的代码方案
你说得没错,async_read_some确实没有直接接受streambuf的重载,但我们可以通过streambuf的prepare/commit方法来绕开这个限制,同时实现“合并之前剩余数据+新读数据”的需求。下面是修改后的完整代码示例:
class io { // 这里可以补充构造函数等初始化逻辑,比如初始化socket和io_context public: void read_until() { boost::asio::async_read_until(m_socket, m_buffer, "\n", [this](const boost::system::error_code& ec, std::size_t bytes_transferred) { if (!ec) { // 先处理async_read_until读到的换行分隔数据(可选操作) std::istream input_stream(&m_buffer); std::string line; std::getline(input_stream, line); // 此时m_buffer的可读区域已经剩下换行符之后的未处理数据 // 继续调用async_read_some读取后续数据 read_some(); } // 别忘了处理错误场景,比如连接断开等 }); } void read_some() { // 第一步:预分配可写缓冲区空间(这里选1024字节,可根据业务调整) auto write_buffer = m_buffer.prepare(1024); // 第二步:用预分配的缓冲区调用async_read_some m_socket.async_read_some(write_buffer, [this](const boost::system::error_code& ec, std::size_t bytes_transferred) { if (!ec && bytes_transferred > 0) { // 第三步:把实际读到的数据从可写区提交到可读区 m_buffer.commit(bytes_transferred); // 现在可读区里就是:async_read_until剩余的数据 + 本次async_read_some读到的新数据 std::istream input_stream(&m_buffer); std::string merged_data; // 按需处理合并后的数据,比如读取所有可用内容 std::getline(input_stream, merged_data); // 或者用其他方式读取 // 如果需要持续读取,递归调用read_some read_some(); } // 处理错误场景 }); } private: boost::asio::streambuf m_buffer; boost::asio::ip::tcp::socket m_socket; };
关键步骤解释
prepare(n):给streambuf的可写区域预分配n字节的空间,返回一个可变缓冲区序列,供async_read_some写入数据。如果当前可写区剩余空间不足,streambuf会自动扩容。commit(bytes_transferred):把可写区中实际写入的bytes_transferred字节,从逻辑上转移到可读区域的末尾。注意这不是内存拷贝,只是移动内部指针,效率很高。- 提交完成后,用
std::istream访问streambuf,就能拿到合并后的所有数据了。
二、关于streambuf内部结构的疑问解答
针对你提出的几个问题,我逐个给你明确答案:
- Q1: boost::asio::streambuf是否至少包含两个缓冲区:读缓冲区和写缓冲区?
对的!streambuf内部维护了两个逻辑区域:可读区域(read area)和可写区域(write area)。底层可能由多个连续内存块组成,但对外暴露为这两个逻辑区域,方便我们按读写场景操作。 - Q2: streambuf::prepare是否是在写缓冲区分配空间?
完全正确。prepare(n)的作用就是在可写区域预留n字节的可用空间,返回的缓冲区指针直接指向这块预留空间,供写入操作(比如async_read_some)使用。 - Q3: streambuf::commit是否是把数据从写缓冲区拷贝到读缓冲区?
不是拷贝,是移动逻辑边界指针。streambuf用内部指针标记读区和写区的起始/结束位置,commit操作只是把写区中已写入数据的边界移动,将这部分数据纳入可读区域的范围,全程没有内存拷贝,性能很高。 - Q4: streambuf::consume是否是从读缓冲区删除数据?
同样是移动逻辑指针,不是真正删除内存。consume(n)会把可读区域的起始指针往后移动n字节,前面的n字节就不再被视为可读数据了。当这些内存块不再被任何区域引用时,streambuf会自动回收内存。 - Q5: 如果读缓冲区已有[my_data],执行prepare(n)、async_read_some、commit后,读缓冲区是否会变成[my_data]+[new_data]?
没错!commit操作会把新写入写区的数据追加到可读区域的末尾,所以最终可读区的内容就是原数据加上新读的数据,顺序完全保持一致。
内容来源于stack exchange
相关产品推荐
相关产品推荐

