协程HTTP客户端需基于Boost::beast实现,返回boost::asio::awaitable类型,重载operator co_await以支持异步I/O;必须使用beast::http::parser解析响应,配合zlib处理gzip,并显式检查error_code与HTTP状态码,超时由socket设置,DNS解析须异步化。

协程函数必须返回 task 或类似可等待类型
直接用 co_await 发起 HTTP 请求的前提是:被 await 的对象得满足 *awaitable* 协议。标准库没提供 HTTP 客户端,所以你不能写 co_await http_get("https://api.example.com") 就完事——这会编译失败,因为返回类型不是可等待的。
实际做法是封装一个自定义类型(比如 http_request),重载 operator co_await,内部触发非阻塞 socket 操作,并在 I/O 完成时唤醒协程。主流方案如 libcurl + curl_easy_setopt(..., CURLOPT_XFERINFOFUNCTION, ...) 配合事件循环;更轻量的选型是 boost::asio 的 async_connect / async_read,再包装成 awaitable。
- 别试图把同步
curl_easy_perform直接塞进协程里——它会阻塞整个线程,失去异步意义 -
std::coroutine_handle本身不调度,你得自己配事件循环(比如boost::asio::io_context::run()) - 返回类型推荐用
boost::asio::awaitable<std::string>而非裸task<std::string>,避免重复造轮子
HTTP 解析不能靠 std::string::find 硬拆包
协程暂停点通常设在读取响应头或响应体时,但 HTTP 协议有分块传输、gzip 压缩、连接复用等特性。如果只等固定字节数或简单扫描 "\r\n\r\n",大概率在 chunked 编码或压缩响应里出错——要么提前结束,要么卡死。
真正可行的是复用成熟解析器,比如 http_parser(C 实现,零依赖)或 boost::beast::http::parser。后者与 boost::asio 协程天然契合,parser::get().body().data() 可直接绑定到协程的缓冲区。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
boost::beast::http::request<boost::beast::http::string_body>和response<boost::beast::http::dynamic_body>是常用组合 - 别在协程里手动管理
Content-Length计数——解析器已处理好边界,你只需co_await parser.async_wait(...) - gzip 响应要额外启用
boost::beast::zlib::flate_stream,否则parser会把压缩流当纯文本解析
co_await 后的异常必须显式捕获
HTTP 请求失败不会自动 throw 异常,而是通过 boost::system::error_code 返回。如果你写 auto resp = co_await http_get(...); 却没检查 resp.result() 或 ec,程序可能静默返回空字符串或崩溃(比如访问 parser.body().data() 前未验证解析状态)。
正确模式是:所有 co_await 调用后立即检查错误,且把网络层错误映射为业务可识别的枚举(如 http_error::connection_refused),而不是裸抛 std::system_error。
-
co_await本身不传播异常,但await_resume()里若 throw,协程会终止并沿调用栈冒泡 - 超时必须由底层 socket 设置(如
socket.expires_after(30s)),协程内无法靠std::this_thread::sleep_for实现 - 重试逻辑要放在协程外(比如 while 循环套协程调用),否则每次重试都新建协程帧,开销不可控
多个并发请求别直接 co_await 串行写
写成 co_await req1(); co_await req2(); co_await req3(); 是顺序执行,耗时是三者之和。真要并发,得用 boost::asio::when_all 或手写 std::vector<awaitable<T>> 收集后 co_await when_all(...)。
注意:并发数不是越多越好。操作系统对 socket 数量有限制(ulimit -n),boost::asio::io_context 默认单线程,高并发下需配合 thread_pool 或 strand 避免数据竞争。
-
when_all返回的是std::tuple,不是std::vector,解包得用结构化绑定或std::get<0>(result) - 每个请求应独占 socket,复用连接需用
keep-alive+ 连接池,否则频繁建连反而更慢 - 协程栈默认很小(几十 KB),大量并发请求易触发栈溢出,建议用
boost::asio::experimental::make_parallel_group控制并发度
最麻烦的其实是 DNS 解析——boost::asio::ip::tcp::resolver::async_resolve 也得协程化,且系统 getaddrinfo 可能阻塞,生产环境务必替换为异步 DNS 库(如 c-ares)。这点容易被忽略,一查域名就卡住整个协程调度器。

















