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

Boost Asio async_read_some使用失败及TCP整包读取方案咨询

关于Boost Asio异步读取非格式化数据及可变长TCP数据包的问题解决

问题根源

你之前用async_read_until基于\n读取数据的方案仅适用于文本格式化数据,改用async_read_some后代码无响应、回调不触发,核心问题出在Defer包装的Socket与回调函数不兼容:

  • 你定义的Socket是deferred_t::as_default_on_t<tcp::socket>,这类对象的异步操作默认返回awaitable,而非接受传统回调函数。直接传入handler不会触发操作执行,反而会导致未定义行为。
  • 循环中反复调用async_read_some却不等待操作完成,会无限提交未执行的异步任务,同时阻塞线程。

修复后的async_read_some异步读取代码

修正代码,利用协程的co_await等待异步操作完成,无需手动传入回调:

#include <boost/asio.hpp>
#include <iostream>

namespace asio = boost::asio;
namespace this_coro = asio::this_coro;
using asio::ip::tcp;

template <typename T> using Defer = asio::deferred_t::as_default_on_t<T>;
using Socket = Defer<tcp::socket>;
using Acceptor = Defer<tcp::acceptor>;

asio::awaitable<void> session(Socket socket)
{
    auto ep = socket.remote_endpoint();
    std::cout << "New session for " << ep << "\n";

    size_t requests_handled = 0;
    try {
        char data[32];
        for (;; ++requests_handled) {
            // 用co_await等待异步读取完成,获取读取字节数
            std::size_t bytes_read = co_await socket.async_read_some(asio::buffer(data, sizeof(data)));
            std::cout << "Read " << bytes_read << " bytes: " << std::string(data, bytes_read) << "\n";
        }
    } catch (const boost::system::system_error& e) {
        std::cout << "Session " << ep << " closed: " << e.what() << "\n";
    }
    std::cout << " -- Session " << ep << " handled " << requests_handled << " requests" << std::endl;
}

asio::awaitable<void> listener(uint16_t port)
{
    auto ex = co_await this_coro::executor;
    Acceptor acc(ex, { {}, port });
    for (;;) {
        co_spawn(ex, session(co_await acc.async_accept()), asio::detached);
    }
}

int main()
{
    asio::io_context ioc(1);
    co_spawn(ioc, listener(8989), asio::detached);
    ioc.run();
}

读取可变长完整TCP数据包的实现方案

TCP是流协议,没有天然的数据包边界,要读取完整可变长数据包,最可靠的方式是先读取固定长度的头部(包含数据包总长度),再读取对应长度的正文:

示例代码(基于头部长度的完整数据包读取)

#include <boost/asio.hpp>
#include <iostream>
#include <vector>

namespace asio = boost::asio;
namespace this_coro = asio::this_coro;
using asio::ip::tcp;

template <typename T> using Defer = asio::deferred_t::as_default_on_t<T>;
using Socket = Defer<tcp::socket>;
using Acceptor = Defer<tcp::acceptor>;

// 读取固定长度的数据,处理TCP流分段情况
asio::awaitable<std::size_t> read_exact(Socket& sock, asio::mutable_buffer buf)
{
    std::size_t total_read = 0;
    while (total_read < asio::buffer_size(buf)) {
        std::size_t bytes_read = co_await sock.async_read_some(
            asio::buffer(buf + total_read, asio::buffer_size(buf) - total_read)
        );
        total_read += bytes_read;
    }
    co_return total_read;
}

asio::awaitable<void> session(Socket socket)
{
    auto ep = socket.remote_endpoint();
    std::cout << "New session for " << ep << "\n";

    try {
        while (true) {
            // 1. 读取4字节的头部(存储数据包长度,网络字节序)
            uint32_t payload_len;
            co_await read_exact(socket, asio::buffer(&payload_len, sizeof(payload_len)));
            payload_len = ntohl(payload_len); // 转换为主机字节序

            // 2. 根据长度读取完整数据包内容
            std::vector<char> payload(payload_len);
            co_await read_exact(socket, asio::buffer(payload));

            std::cout << "Received complete packet (" << payload_len << " bytes): " 
                      << std::string(payload.begin(), payload.end()) << "\n";
        }
    } catch (const boost::system::system_error& e) {
        std::cout << "Session " << ep << " closed: " << e.what() << "\n";
    }
}

asio::awaitable<void> listener(uint16_t port)
{
    auto ex = co_await this_coro::executor;
    Acceptor acc(ex, { {}, port });
    for (;;) {
        co_spawn(ex, session(co_await acc.async_accept()), asio::detached);
    }
}

int main()
{
    asio::io_context ioc(1);
    co_spawn(ioc, listener(8989), asio::detached);
    ioc.run();
}

说明

  • read_exact函数确保读取指定长度的数据,处理TCP流中数据分段的情况。
  • 头部长度可根据协议调整(如2字节、8字节),注意字节序转换(网络字节序转主机字节序)。
  • 如果协议没有头部,需自定义边界标记,但这种方式可靠性较低,仅适用于特定场景。

内容的提问来源于stack exchange,提问作者Jelal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 11:27:36