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

使用boost::beast解析HTTP分块流时遇"stream truncated"错误求助

使用Boost.Beast通过HTTPS流获取EXANTE API订单流时的周期性中断问题

我用boost::beast实现了通过HTTPS流获取EXANTE API的订单流数据,代码几乎和Beast库官方「Chunked encoding」示例一致。运行基本正常,但读取分块时会周期性被Stream truncated或Partial message错误中断,无法确定是代码问题、网络连接问题、服务器问题,还是代理/防火墙导致的连接中断。

更新:Wireshark流量分析显示,错误出现前会收到TLS消息Encrypted alert (21),但不清楚其含义及解读方式。

以下是最简版本的代码(测试账号数据,无保密问题):

#include <string>
#include <iostream>

#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/algorithm/string.hpp>

#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>

#include <boost/beast.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/http.hpp>

using namespace std::string_literals;
namespace net   = boost::asio;
namespace ssl   = boost::asio::ssl;
namespace ip    = boost::asio::ip;
namespace beast = boost::beast;
namespace http  = boost::beast::http;
using tcp = boost::asio::ip::tcp;

std::string base64_encode(const std::string& val) {
    using namespace boost::archive::iterators;
    using It = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>;
    auto tmp = std::string(It(std::begin(val)), It(std::end(val)));
    return tmp.append((3 - val.size() % 3) % 3, '=');
}

int main() {

    const auto clientID = "6dc42e50-0b01-444e-bae0-3a431c88b525"s;
    const auto key = "TTQmWnbHlr5OWlNSWl1P"s;
    const auto host = "api-demo.exante.eu"s;
    const auto target = "/trade/3.0/stream/orders"s;
    const auto port = "443"s;
    const auto http_version = 11;
    const auto auth_token = "Basic "s + base64_encode(clientID + ':' + key);
    const auto accept_value = "application/x-json-stream"s;
    const auto timeout = std::chrono::seconds(12);

    http::request<http::string_body> request{http::verb::get, target, http_version};
    request.set(http::field::host, host);
    request.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
    request.set(http::field::authorization, auth_token);
    request.set(http::field::accept, accept_value);
    request.prepare_payload();

    net::io_context ioc;

    ssl::context ctx(ssl::context::tlsv12_client);
    tcp::resolver resolver(ioc);
    beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);
    if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
        beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()};
        throw beast::system_error{ec};
    }
    auto const results = resolver.resolve(host, port);

    beast::get_lowest_layer(stream).connect(results);
    stream.handshake(ssl::stream_base::client);

    http::write(stream, request);

    beast::flat_buffer buffer;
    http::parser<false, http::empty_body> p;

    http::read_header(stream, buffer, p);

    http::chunk_extensions ce;
    std::string chunk;

    auto header_cb =
            [&](std::uint64_t size,
                beast::string_view extensions,
                beast::error_code &ev) {
                ce.parse(extensions, ev);
                if (ev)
                    return;

                if (size > (std::numeric_limits<std::size_t>::max)()) {
                    ev = http::error::body_limit;
                    return;
                }

                chunk.reserve(static_cast<std::size_t>(size));
                chunk.clear();
            };
    p.on_chunk_header(header_cb);

    auto body_cb =
            [&](std::uint64_t remain,
                beast::string_view body,
                beast::error_code &ec) {
                if (remain == body.size())
                    ec = http::error::end_of_chunk;

                chunk.append(body.data(), body.size());

                return body.size();
            };
    p.on_chunk_body(body_cb);

    std::function<void(const beast::error_code &, size_t)> onRead = [&](const auto &ec, auto bytes_transferred) {
        if (ec && ec != http::error::end_of_chunk)
            throw std::runtime_error(ec.what());

        for (auto const &extension: ce) {
            std::cout << "Extension: " << extension.first;
            if (!extension.second.empty())
                std::cout << " = " << extension.second << std::endl;
            else
                std::cout << std::endl;
        }

        static int counter = 0;
        std::cout << "Chunk Body: " << chunk << counter++ << " @@@!" << std::endl;
        chunk.clear();
        if (!p.is_done()) {
            stream.next_layer().expires_after(timeout);
            http::async_read(stream, buffer, p, onRead);
        }
    };

    stream.next_layer().expires_after(timeout);
    http::async_read(stream, buffer, p, onRead);

    ioc.run();
    
    return 0;
}

我尝试过http::read的同步和异步版本,问题均会出现。该错误在三台不同设备、不同网络环境下都会发生。据API开发者称,HTTP流监听时长无限制,错误出现间隔随机,从几分钟到数小时不等。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 22:47:16