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