boost::asio::async_read_until读取socket数据过量致async_read阻塞问题求助
问题与解决方案
问题描述
需要处理格式为<size> <data>的TCP消息:<size>是<data>的长度整数,两者以空格分隔(示例:19 {"command":"start"})。使用Boost.Asio的async_read_until读取到空格后,通过Wireshark确认整个消息在单个TCP帧中,但处理完size和空格后,缓冲区已包含完整的data数据,调用async_read(..., transfer_exactly(data_size), ...)却持续阻塞,直到收到新数据才触发回调。
核心原因
transfer_exactly(n)的作用是要求从socket读取累计n字节,而非从缓冲区中读取已有数据。即使缓冲区里已有足够的data,该操作仍会等待socket发送新数据,导致阻塞。
解决步骤
正确处理streambuf的提交与消费
- 在
async_read_until的回调中,先调用_buffer.commit(bytes_received),将接收到的字节从输入序列转移到输出序列,才能读取内容。 - 读取完size和空格后,调用
_buffer.consume(处理字节数)(比如示例中处理了3字节的19,就调用consume(3)),此时缓冲区剩余内容即为data的起始部分。
- 在
检查缓冲区已有数据,按需读取
- 计算缓冲区中已有的data字节数:
available = _buffer.size() - 计算还需从socket读取的字节数:
remaining = data_size - available- 若
remaining <= 0:直接从缓冲区提取完整data,处理后消费对应字节,继续接收下一条消息。 - 若
remaining > 0:调用async_read读取剩余的remaining字节,而非整个data_size。
- 若
- 计算缓冲区中已有的data字节数:
代码示例
void Command::OnSize(const boost::system::error_code& error, std::size_t bytes_received) { if (error) { // 错误处理逻辑 return; } // 提交接收到的字节到输出序列 _buffer.commit(bytes_received); std::istream is(&_buffer); std::size_t data_size; char space; // 读取size和分隔空格 is >> data_size >> space; std::size_t available = _buffer.size(); std::size_t remaining = data_size - available; if (remaining <= 0) { // 缓冲区已有完整数据,直接处理 ProcessData(data_size); _buffer.consume(data_size); Receive(); // 继续接收下一条消息 } else { // 仅读取剩余缺失的字节 boost::asio::async_read(_socket, _buffer, boost::asio::transfer_exactly(remaining), std::bind(&Command::OnData, this, std::placeholders::_1, std::placeholders::_2, data_size)); } } void Command::OnData(const boost::system::error_code& error, std::size_t bytes_received, std::size_t data_size) { if (error) { // 错误处理逻辑 return; } ProcessData(data_size); _buffer.consume(data_size); Receive(); // 继续接收下一条消息 } void Command::ProcessData(std::size_t data_size) { std::vector<char> data(data_size); std::istream is(&_buffer); is.read(data.data(), data_size); // 此处添加data的业务处理逻辑 }
内容的提问来源于stack exchange,提问作者Stewart
相关产品推荐
相关产品推荐

