跳转到主内容
极星编程网:以代码为星,赴技术山海!

C++实现带有超时重试机制的HTTP异步请求 _ Boost.Asio实战应用【源码】

Boost.Asio 本身不提供 HTTP 协议解析,必须手动拼接请求头、解析响应;超时和重试需为每个异步操作(resolve/connect/write/read)单独配置 deadline_timer,超时调用 socket.cancel() 并检查 operation_aborted 错误,重试仅针对 5xx、429、503 等服务端错误,且每次重试须重建 socket 和 timer 对象。 如何用 Boost.Asio 发起带超时和重试的异步 HTTP 请求 直接说结论:Boost.Asio 本身不提供 HTTP 协议解析,必须自己拼接请求头、解析响应;超时和重试得靠
deadline_timer
或 C++11
steady_clock
+
async_wait
手动控制,不能依赖
socket::set_option(socket_base::keep_alive)
—— 那只是 TCP 层保活,对 HTTP 超时不生效。 为什么 async_connect + async_write + async_read_until 容易卡死 常见错误是把整个 HTTP 流程串成“一步 async_read_until(\n\n)”就完事,结果服务器只返回部分响应、或分块传输(chunked)没处理、或中间网络抖动导致
async_read
永远等不到 \n\n。这时既没超时,也没重试触发点。 必须为每个异步操作(connect / write / read header / read body)单独配一个
deadline_timer
,超时后调用
socket.cancel()
并检查
error == boost::asio::error::operation_aborted
async_read_until
对 \n\n 不可靠:HTTP 响应头末尾是
\r\n\r\n
,不是
\n\n
;且某些代理会改写换行符 推荐用
async_read
循环读取,手动查找
\r\n\r\n
边界,避免缓冲区溢出或死等 重试逻辑该放在哪一层:连接失败?读超时?还是状态码非 2xx? 重试不是越靠后越好。例如 503 Service Unavailable 可重试,但 404 或 401 就不该无脑重试;而 DNS 解析失败(
resolve
阶段 error)或
connect
被拒绝(connection refused),通常值得立即重试 1–2 次。 在
resolver.async_resolve
失败时,若 error 是
boost::asio::error::host_not_found
,可延迟 100ms 后重 resolve
async_connect
超时或失败,建议指数退避:第 1 次等 200ms,第 2 次等 400ms,最多 3 次 收到完整响应后,先检查
status_code
:仅对 5xx 和部分 429/503 做重试;不要对 400、401、403、404 等客户端错误重试 每次重试前必须销毁旧 socket 和 timer,新建对象,否则资源残留会导致未定义行为 一个最小可行的异步请求结构体怎么组织 别写成大单例或全局 handler。每个请求应封装为独立对象(如
http_request
),持有自己的
tcp::socket
、
deadline_timer
、buffer 和重试计数器。生命周期由
shared_ptr
管理,避免 handler 被销毁后回调还访问野指针。 C知道 CSDN推出的一款AI技术问答工具 下载 立即学习 “ C++免费学习笔记(深入) ”;
struct http_request { tcp::socket sock_; deadline_timer timer_; std::string host_, port_, path_; int retry_count_ = 0; static const int MAX_RETRY = 3; void start() { resolver_.async_resolve(host_, port_, [self = shared_from_this()](error_code ec, results_type res) { if (ec) { self->on_resolve_fail(ec); return; } self->start_connect(res); }); } void start_connect(results_type res) { timer_.expires_from_now(boost::posix_time::milliseconds(5000)); timer_.async_wait([self = shared_from_this()](error_code ec) { if (!ec) self->sock_.cancel(); // 触发 connect 的 operation_aborted }); boost::asio::async_connect(sock_, res, [self = shared_from_this()](error_code ec, endpoint_type) { if (ec == boost::asio::error::operation_aborted) { if (++self->retry_count_ <= MAX_RETRY) self->start(); return; } if (!ec) self->send_request(); }); } // … 后续 send_request / read_header / read_body 类似 };
注意
self = shared_from_this()
必须在捕获列表里显式写出,否则 lambda 持有的 this 可能悬空;
timer_.async_wait
和
sock_.cancel()
配合是唯一可靠的超时中断手段。

相关文章