使用Boost异步读取HTTP分块响应体时编译错误求助
Boost Beast异步读取HTTP分块响应体编译错误解决
问题描述
尝试用Boost异步读取HTTP分块响应体,已成功读取响应头,但读取响应体时编译报错。最小示例代码如下:
void body_read_loop() { http::response_parser<http::buffer_body> parser_ = http::response_parser<http::buffer_body>(); async_read(*sslSocket, buf, parser_, [this](error_code ec, size_t) { }); }
编译时抛出错误:
Call to deleted constructor of 'boost::beast::http::parser<false, boost::beast::http::buffer_body>'
疑惑点:调用的async_read函数明明是按引用传入parser,函数签名如下:
template< class AsyncReadStream, class DynamicBuffer, bool isRequest, BOOST_BEAST_ASYNC_TPARAM2 ReadHandler> BOOST_BEAST_ASYNC_RESULT2(ReadHandler) async_read( AsyncReadStream& stream, DynamicBuffer& buffer, basic_parser<isRequest>& parser, ReadHandler&& handler) {
完整可复现代码(Ubuntu+Boost 1.74.0环境)
#include <boost/beast.hpp> #include <iostream> #include <span> namespace net = boost::asio; namespace beast = boost::beast; namespace http = beast::http; using boost::system::error_code; using net::ip::tcp; static void check(error_code& ec) { if (ec && ec != http::error::need_buffer) // expected throw boost::system::system_error(ec); } template <typename Derived> struct CommonImpl { net::io_context ioc; tcp::socket s{ioc}; tcp::resolver r{ioc}; http::request<http::empty_body> req{http::verb::get, "/stream-bytes/2000?seed=42", 11}; void run() { r.async_resolve( "httpbin.org", "http", beast::bind_front_handler(&CommonImpl::on_resolved, this)); ioc.run(); } void on_resolved(error_code ec, tcp::resolver::results_type eps) { check(ec); async_connect(s, eps, [this](error_code ec, tcp::endpoint) { check(ec); req.set(http::field::host, "httpbin.org"); async_write(s, req, beast::bind_front_handler(&CommonImpl::on_sent, this)); }); } void on_sent(error_code ec, size_t) { check(ec); static_cast<Derived&>(*this).do_read_response(); } }; struct BufferImpl : CommonImpl<BufferImpl> { http::response_parser<http::buffer_body> p; http::response<http::buffer_body>& res = p.get(); http::buffer_body::value_type& body_val = res.body(); beast::flat_buffer buf; void do_read_response() { async_read_header(s, buf, p, beast::bind_front_handler(&BufferImpl::on_headers, this)); } void on_headers(error_code ec, size_t) { check(ec); assert(p.is_header_done()); std::cout << "\n---\nresponse headers: " << res.base() << std::endl; body_read_loop(); } size_t checksum = 0; size_t n = 0; std::array<uint8_t, 512> block; void body_read_loop() { if (!p.is_done()) { body_val.data = block.data(); body_val.size = block.size(); async_read(s, buf, p, [this](error_code ec, size_t) { check(ec); size_t decoded = block.size() - body_val.size; n += decoded; std::cout << "parsed " << decoded << " body bytes\n"; for (auto b : std::span(block).first(decoded)) checksum ^= b; body_read_loop(); }); } else { // fmt::print("buffer body, {} bytes streaming decoded, chunked? {}\n", n, p.chunked()); // fmt::print("buffer body checksum: {:#0x}\n", checksum); } } }; int main(int argc, char **argv) { BufferImpl{}.run(); return 0; }
错误原因及解决方法
编译错误的核心原因是**response_parser类禁止拷贝构造与拷贝赋值**,你最初的最小示例中parser_ = http::response_parser<http::buffer_body>();这行代码试图执行拷贝赋值操作,触发了被删除的拷贝构造函数。
你的完整MCVE代码其实已经规避了这个问题(成员变量p是直接初始化的),但仍需注意两个关键点:
- 确保
response_parser对象的生命周期覆盖整个异步操作流程,不能在异步回调完成前被销毁 - 永远不要尝试拷贝或赋值
response_parser实例,始终通过引用传递
针对最初的最小示例,修正方式为直接默认初始化parser,避免拷贝操作:
void body_read_loop() { http::response_parser<http::buffer_body> parser_; // 直接默认初始化 async_read(*sslSocket, buf, parser_, [this](error_code ec, size_t) { // 处理逻辑 }); }
另外,使用buffer_body处理分块响应时,每次调用async_read前都要设置好有效的缓冲区(如你在MCVE的body_read_loop中所做),确保读取操作有可用的内存空间。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

