c++如何使用Coroutines TS与Asio结合_c++协程网络编程实践

1次阅读

使用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结合_c++协程网络编程实践

使用 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_awaitasio::use_awaitable。它告诉 Asio 以协程方式等待异步操作完成,而不是使用回调函数

启动协程服务

你需要一个 asio::io_context 来运行事件循环,并通过协程调度任务。

示例服务器主循环:

c++如何使用Coroutines TS与Asio结合_c++协程网络编程实践

Motiff

Motiff是由猿辅导旗下的一款界面设计工具,定位为“AI时代设计工具”

c++如何使用Coroutines TS与Asio结合_c++协程网络编程实践 126

查看详情 c++如何使用Coroutines TS与Asio结合_c++协程网络编程实践

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++ 协程,能写出既高效又易维护的网络服务。不复杂但容易忽略细节,比如错误处理和执行器上下文传递。

text=ZqhQzanResources