不能,c++20协程本身不提供网络能力,co_await仅为挂起/恢复语法糖,发送http请求必须依赖boost::beast+asio::awaitable等封装好的异步i/o库或成熟http客户端库。

协程能直接发 HTTP 请求吗?不能,得靠库封装
标准 C++20 协程本身不提供网络能力,co_await 只是挂起/恢复机制。真正发请求必须依赖底层 I/O 调度器(比如 libuv、asio)或封装好的异步 HTTP 库。硬写 socket + 协程调度器成本极高,不推荐从零实现。
实际可行路径只有两条:用支持协程的成熟 HTTP 客户端库,或在 boost::asio / libcurl(带异步回调)基础上自己封装 awaitable。前者开发快、稳定;后者可控性强但容易翻车。
- 推荐首选
cpp-httplib的协程分支(如 PR #1396),但它只支持同步阻塞模式 → 需配合线程池 +co_spawn模拟“轻量”,不是真异步 -
boost::beast+boost::asio::awaitable是目前最稳妥的组合,支持真正的异步、零拷贝、SSL,且无需额外线程池 - 别碰
libcurl的 multi 接口 + 协程封装——它的事件循环和协程生命周期难对齐,CURLOPT_WRITEFUNCTION回调里co_await极易导致悬垂引用或崩溃
boost::beast + asio::awaitable 怎么发 GET 请求
这是当前 C++ 中最接近“开箱即用”的方案。核心是把 http::async_read 和 http::async_write 封装成 awaitable,再用 co_await 驱动。注意:必须启用 C++20,并链接 boost_system 和 boost_coroutine(部分版本还需 boost_context)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
boost::asio::awaitable<:string> http_get(const std::string& host, const std::string& target) {
auto executor = co_await boost::asio::this_coro::executor;
boost::asio::ip::tcp::resolver resolver(executor);
boost::asio::ip::tcp::socket socket(executor);
auto results = co_await resolver.async_resolve(host, "80", boost::asio::use_awaitable);
co_await boost::asio::async_connect(socket, results.begin(), results.end(), boost::asio::use_awaitable);
http::request<:string_body> req{http::verb::get, target, 11};
req.set(http::field::host, host);
req.set(http::field::user_agent, "C++20-coroutine-client");
co_await http::async_write(socket, req, boost::asio::use_awaitable);
flat_buffer buffer;
http::response<:dynamic_body> res;
co_await http::async_read(socket, buffer, res, boost::asio::use_awaitable);
co_return boost::beast::buffers_to_string(res.body().data());
}</:dynamic_body></:string_body></:string>
- 必须用
flat_buffer(不是boost::beast::multi_buffer),否则async_read在协程恢复时可能读到脏内存 -
http::response<:dynamic_body></:dynamic_body>是安全选择;若用string_body,需确保响应体不大于栈空间,否则触发std::bad_alloc - 别漏掉
co_return——awaitable函数返回类型必须严格匹配,否则编译报错如no matching function for call to 'await_transform'
如何避免协程堆分配和上下文切换开销
所谓“轻量级”,关键在减少堆内存分配和避免跨线程调度。Beast 默认会为每个 awaitable 操作分配一个 executor 内部状态对象,但可通过 boost::asio::prefer 控制资源策略。
- 用
boost::asio::execution::prefer(boost::asio::execution::occupancy, 1)强制单栈帧,禁用内部队列 - 所有 I/O 对象(
socket,resolver,buffer)声明在协程函数栈上,不要new或传入 shared_ptr —— 否则协程挂起后对象可能被提前析构 - 避免在协程中捕获大型对象(比如整个
http::request实例),改用std::move或引用传参;否则每次挂起都复制整个对象 - 如果并发量大,用
boost::asio::thread_pool替代io_context::run(),并设置线程数 ≤ CPU 核心数,防止上下文切换反拖慢性能
SSL 请求为什么总是 handshake_failed
Beast 的 SSL 支持需要显式配置 ssl::stream 和证书验证逻辑,默认拒绝自签名证书。常见错误不是代码写错,而是环境缺失。
- 必须调用
ctx.set_verify_mode(ssl::verify_none)(测试用)或ctx.add_certificate_authority(...)(生产用),否则握手直接失败,错误码是sslv3 alert handshake failure - 证书路径要用绝对路径,相对路径在协程切换后可能失效;建议用
std::filesystem::absolute("ca-bundle.crt")显式转换 - SSL socket 的
async_handshake必须在async_connect之后、async_write之前调用,顺序错会导致asio.misc:12(invalid argument) - 别用
ssl::context::tlsv13—— 很多服务器还不支持,降级到ssl::context::tls更兼容
协程的“轻量”是相对线程而言,但 HTTP 解析、SSL 握手、DNS 查询这些操作本身有不可省略的开销。真正要注意的是别让协程变成“假异步”——比如用阻塞 DNS 查询混在协程里,或者把整个请求包丢给线程池再 co_await,那就失去了协程调度的意义。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










