You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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发送新数据,导致阻塞。

解决步骤

  1. 正确处理streambuf的提交与消费

    • 在async_read_until的回调中,先调用_buffer.commit(bytes_received),将接收到的字节从输入序列转移到输出序列,才能读取内容。
    • 读取完size和空格后,调用_buffer.consume(处理字节数)(比如示例中处理了3字节的19 ,就调用consume(3)),此时缓冲区剩余内容即为data的起始部分。
  2. 检查缓冲区已有数据,按需读取

    • 计算缓冲区中已有的data字节数:available = _buffer.size()
    • 计算还需从socket读取的字节数:remaining = data_size - available
      • 若remaining <= 0:直接从缓冲区提取完整data,处理后消费对应字节,继续接收下一条消息。
      • 若remaining > 0:调用async_read读取剩余的remaining字节,而非整个data_size。

代码示例

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 19:07:33