在传统的 C++ 网络编程中,异步 IO 通常需要使用回调、Future/Promise、或第三方库(如 Boost.Asio)来实现。协程(Coroutine)作为 C++20 标准的一部分,为异步代码提供了更直观、可维护的写法。下面以一个简单的 TCP 服务器为例,演示如何利用协程实现异步读写,突出其优势与实现细节。
-
协程基础概念回顾
co_await:暂停协程并等待一个 awaitable 对象完成。co_return:结束协程并返回结果。- `awaitable `:自定义 awaitable,必须实现 `await_ready()`、`await_suspend()`、`await_resume()` 三个成员。
-
自定义 awaitable:异步读取
template<class Socket> struct async_read { Socket& sock_; std::vector <char> buf_; async_read(Socket& s, std::size_t size) : sock_(s), buf_(size) {} bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<> h) { sock_.async_read_some(boost::asio::buffer(buf_), [h](const boost::system::error_code& ec, std::size_t n){ // 这里把错误码和读取长度存到协程的状态 // 假设我们在协程类里存储 ec 和 n h.resume(); }); } std::vector <char> await_resume() { // 这里假设协程类里已存储 ec, n if (ec) throw boost::system::system_error(ec); return std::move(buf_); } }; -
异步写入 awaitable
template<class Socket> struct async_write { Socket& sock_; const std::vector <char>& data_; async_write(Socket& s, const std::vector <char>& d) : sock_(s), data_(d) {} bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<> h) { boost::asio::async_write(sock_, boost::asio::buffer(data_), [h](const boost::system::error_code& ec, std::size_t){ h.resume(); }); } void await_resume() { // 处理错误 if (ec) throw boost::system::system_error(ec); } }; -
协程服务器核心逻辑
struct session { tcp::socket socket_; session(tcp::socket s) : socket_(std::move(s)) {} // 业务逻辑协程 std::future <void> operator()() { try { while (true) { auto data = co_await async_read{socket_, 1024}; // 这里可以做任何业务处理 std::transform(data.begin(), data.end(), data.begin(), ::toupper); co_await async_write{socket_, data}; } } catch (const std::exception& e) { std::cerr << "Session error: " << e.what() << '\n'; } } }; -
整合与启动
void server(io_context& ctx, unsigned short port) { tcp::acceptor acceptor(ctx, tcp::endpoint(tcp::v4(), port)); while (true) { tcp::socket sock = co_await acceptor.async_accept(); std::make_shared <session>(std::move(sock))->operator()(); } }
优势对比
- 可读性:异步逻辑像同步代码一样线性书写,避免回调地狱。
- 错误处理:统一使用异常捕获,错误传播更自然。
- 性能:协程的状态机由编译器生成,消除了运行时的栈切换开销。
- 可组合性:不同 awaitable 可以自由组合,满足多种 I/O 场景。
实现细节提示
- 必须为异步操作返回 `boost::asio::awaitable ` 或自定义 awaitable,保证 `co_await` 的兼容性。
- 在高并发场景下,应考虑
io_context的线程池配置,防止协程堆栈溢出。 - 对于复杂业务,可进一步封装
async_read_until、async_read_n等更高级的 awaitable。
结语
C++20 协程为异步网络编程提供了更高层次的抽象,使代码既简洁又高效。随着标准库与第三方库的持续完善,未来协程将成为 C++ 开发者处理异步任务的首选工具。