使用c++20协程与Asio可简化异步网络编程,通过asio::awaitable和co_await实现同步风格代码。1. 需GCC 10+/Clang 12+并启用-std=c++20;2. Asio 1.20+或Boost.Asio 1.75+支持协程;3. 协程返回类型为asio::awaitable,用co_await等待异步操作;4. co_spawn启动协程并指定执行策略如detached;5. 使用asio::redirect_error避免异常抛出,提升控制粒度;6. 注意避免协程中大栈分配和阻塞调用,应使用asio提供的异步机制。

使用 C++ Coroutines TS 与 Asio 进行网络编程,可以极大简化异步操作的编写方式,让异步代码看起来像同步一样清晰。Asio 自 1.20 版本(对应 Boost.Asio 1.75+)开始原生支持 C++20 协程,允许你以现代 C++ 的方式编写高性能异步网络服务。
启用协程支持
确保你的编译环境支持 C++20 并开启协程功能:
- 使用 GCC 10+ 或 Clang 12+ 编译器
- 编译选项添加:
-std=c++20 - Asio 需要定义宏:
ASIO_ENABLE_HANDLER_TRACKING(可选调试)和确保启用了协程支持(默认已启用)
注意:如果你使用的是 Boost.Asio,需使用 Boost 1.75 及以上版本,并包含头文件时使用 #include <boost></boost>。
基本协程返回类型:awaitable
在 Asio 中,协程函数返回类型为 asio::awaitable<t></t>,其中 T 是协程最终返回的值类型(通常为 void)。
一个典型的协程如下:
立即学习“C++免费学习笔记(深入)”;
asio::awaitable<void> echo_session(asio::ip::tcp::socket socket) { try { char data[1024]; for (;;) { std::size_t n = co_await socket.async_read_some(asio::buffer(data), asio::use_awaitable); co_await async_write(socket, asio::buffer(data, n), asio::use_awaitable); } } catch (const std::exception&) { // 客户端断开或出错 } }
这里的关键是 co_await 和 asio::use_awaitable。它告诉 Asio 以协程方式等待异步操作完成,而不是使用回调函数。
启动协程服务
你需要一个 asio::io_context 来运行事件循环,并通过协程调度任务。
示例服务器主循环:
asio::awaitable<void> listen_loop(asio::io_context& ioc, unsigned short port) { auto executor = ioc.get_executor(); asio::ip::tcp::acceptor acceptor(executor, {asio::ip::tcp::v4(), port}); for (;;) { asio::ip::tcp::socket socket = co_await acceptor.async_accept(asio::use_awaitable); co_spawn(executor, echo_session(std::move(socket)), asio::detached); } } int main() { asio::io_context ioc{1}; co_spawn(ioc, listen_loop(ioc, 8080), asio::detached); ioc.run(); return 0; }
说明:
-
co_spawn用于启动一个协程,第三个参数指定如何处理协程完成(如asio::detached表示不关心结果) -
co_await acceptor.async_accept(...)暂停协程直到有连接到来 - 每个新连接通过
co_spawn启动独立会话,不会阻塞主监听循环
异常处理与资源管理
协程中抛出的异常会被自动捕获并传递给协程框架。建议在顶层协程中捕获异常,避免崩溃。
改进后的 session:
asio::awaitable<void> echo_session(asio::ip::tcp::socket socket) { try { char data[1024]; while (socket.is_open()) { auto [e, n] = co_await socket.async_read_some( asio::buffer(data), asio::redirect_error(asio::use_awaitable)); if (e) break; // 客户端关闭 auto [write_err] = co_await async_write( socket, asio::buffer(data, n), asio::redirect_error(asio::use_awaitable)); if (write_err) break; } } catch (...) { // 处理未预期异常 } }
使用 asio::redirect_error 可将错误码转为元组形式返回,避免抛异常,适合精细控制流程。
优势与注意事项
优点:
- 代码逻辑清晰,无需嵌套回调
- 局部变量在
co_await后依然有效,状态保持自然 - 易于组合多个异步操作(如认证 + 数据读取)
注意事项:
- 协程栈空间有限,避免在协程中分配大对象(可使用堆或传引用)
- 不要在协程中调用阻塞函数(如
std::this_thread::sleep_for),应使用asio::steady_timer - 协程生命周期由
co_spawn管理策略决定,detached下需确保资源安全释放
基本上就这些。结合 Asio 与 C++ 协程,能写出既高效又易维护的网络服务。不复杂但容易忽略细节,比如错误处理和执行器上下文传递。