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 被销毁后回调还访问野指针。
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_ start();
return;
}
if (!ec) self->send_request();
});
}
// … 后续 send_request / read_header / read_body 类似
};
注意 self = shared_from_this() 必须在捕获列表里显式写出,否则 lambda 持有的 this 可能悬空;timer_.async_wait 和 sock_.cancel() 配合是唯一可靠的超时中断手段。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











