如何在boost::asio::async_write()处理器中安全访问streambuf内容
解决boost::asio::async_write处理器中streambuf日志打印问题
核心问题原因
boost::asio::async_write会消耗streambuf的输入序列(即待发送的数据),发送完成后会调用consume()移动输入序列的起始指针,此时size()返回的是剩余可读取的字节数,因此变为0。尽管底层内存可能仍保留数据,但无法通过size()可靠获取有效长度。
可靠解决方案
方案1:利用handler返回的length参数重置读取指针
async_write的handler会返回实际成功发送的字节数length,这是Boost.Asio保证的可靠值。我们可以通过重置streambuf的读取指针,重新读取对应长度的数据:
boost::asio::async_write( getSocket(), msgStream, [this](boost::system::error_code ec, std::size_t length) { if (!ec) { auto& msgStreamOut = m_messagesOut.front()->buffer(); // 重置读取指针到缓冲区起始位置 msgStreamOut.pubseekpos(0, std::ios_base::in); // 读取length字节的数据到临时缓冲区 std::string log_data; log_data.resize(length); std::istream is(&msgStreamOut); is.read(log_data.data(), length); // 打印日志 printBufferToLog(log_data.data(), length); // 若后续不再需要该缓冲区数据,可恢复原状态(可选) msgStreamOut.consume(length); } });
注意:该方案依赖streambuf底层内存未被释放(默认实现不会在consume()后立即释放内存),若你的场景中streambuf可能被重新分配或清空,建议使用方案2。
方案2:提前复制数据到独立缓冲区
在发起async_write前,将streambuf中的数据复制到一个独立的缓冲区(如std::string或std::vector<char>),并将该缓冲区捕获到handler中直接用于日志:
auto& msgStream = m_messagesOut.front()->buffer(); // 复制streambuf中的数据到独立缓冲区 std::string log_data; std::istream is(&msgStream); log_data.assign(std::istreambuf_iterator<char>(is), std::istreambuf_iterator<char>()); // 重置streambuf的读取指针,确保async_write能正常读取数据 msgStream.pubseekpos(0, std::ios_base::in); boost::asio::async_write( getSocket(), msgStream, [this, log_data = std::move(log_data)](boost::system::error_code ec, std::size_t length) { if (!ec) { // 直接使用副本打印日志 printBufferToLog(log_data.data(), log_data.size()); } });
优势:完全不受streambuf后续状态变化的影响,是最可靠的方案。若发送的数据量较大,需权衡内存开销,但日志记录的必要性通常能覆盖这部分开销。
内容的提问来源于stack exchange,提问作者YesButWhy
相关产品推荐
相关产品推荐

