当前位置: 首页 > news >正文

C++ 服务端进阶(五)—— Connection + 协程:面向对象的异步模型(工程版完整实现)

一、这一篇到底解决什么问题?

在第四篇中,我们已经完成了:

多 Reactor(并发) + 协程(执行)

架构已经是对的了:

Main Reactor(accept)

Sub Reactor(线程) + coroutine


但是代码形态仍然是:

handleConnection(fd, reactor);

👉 问题:

  • ❌ 连接没有归属对象
  • ❌ 状态分散在函数中
  • ❌ 协程逻辑不属于“连接实体”
  • ❌ 不符合真实服务端设计

本篇目标

把模型升级为:

fd ↓ Connection 对象 ↓ run() 协程 ↓ read / process / write

👉 一句话总结:

连接不再是“被处理的 fd”,而是“自带异步执行能力的对象”

二、第四篇 vs 第五篇(关键对比)

第四篇(函数式)

handleConnection(fd, reactor);

第五篇(对象化)

Connection* conn = new Connection(fd, reactor); conn->start();

👉 本质变化:

函数式异步 → 面向对象异步

三、最终项目结构(第五篇)

. ├── Task.h ├── Reactor.h / Reactor.cpp ├── ReactorThread.h / ReactorThread.cpp ├── ReactorThreadPool.h / ReactorThreadPool.cpp ├── SocketUtil.h / SocketUtil.cpp ├── Connection.h / Connection.cpp └── main.cpp

变化说明

👉 相比第四篇:

删除:
Server.h / Server.cpp

新增:
Connection.h / Connection.cpp

👉 原因:

连接处理逻辑从“外部函数” → 收敛到 Connection 内部

四、核心设计(必须理解)


1️⃣ Connection 是“活的对象”

它不只是数据:

fd
buffer

它还包含:

协程执行逻辑(run)

2️⃣ start() vs run()

void start() { run(); }

👉 start = 启动入口
👉 run = 真正协程执行体

3️⃣ 生命周期归 Connection 自己管理

closeSelf()

👉 谁拥有连接,谁负责关闭

五、完整代码实现

1️⃣ Task.h

#ifndef TASK_H #define TASK_H #include <coroutine> #include <exception> struct DetachedTask { struct promise_type { DetachedTask get_return_object() noexcept { return {}; } std::suspend_never initial_suspend() noexcept { return {}; } struct FinalAwaiter { bool await_ready() noexcept { return false; } template <typename Promise> void await_suspend(std::coroutine_handle<Promise> h) noexcept { h.destroy(); } void await_resume() noexcept {} }; FinalAwaiter final_suspend() noexcept { return {}; } void return_void() noexcept {} void unhandled_exception() { std::terminate(); } }; }; #endif

2️⃣ Reactor.h

#ifndef REACTOR_H #define REACTOR_H #include <coroutine> #include <unordered_map> #include <cstdint> class Reactor { public: Reactor(); ~Reactor(); void waitReadable(int fd, std::coroutine_handle<> handle); void waitWritable(int fd, std::coroutine_handle<> handle); void removeFd(int fd); void loop(); class ReadableAwaiter { public: ReadableAwaiter(Reactor& reactor, int fd) : reactor_(reactor), fd_(fd) {} bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<> handle) { reactor_.waitReadable(fd_, handle); } void await_resume() const noexcept {} private: Reactor& reactor_; int fd_; }; class WritableAwaiter { public: WritableAwaiter(Reactor& reactor, int fd) : reactor_(reactor), fd_(fd) {} bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<> handle) { reactor_.waitWritable(fd_, handle); } void await_resume() const noexcept {} private: Reactor& reactor_; int fd_; }; ReadableAwaiter readable(int fd) { return ReadableAwaiter(*this, fd); } WritableAwaiter writable(int fd) { return WritableAwaiter(*this, fd); } private: struct Entry { std::coroutine_handle<> readHandle{}; std::coroutine_handle<> writeHandle{}; uint32_t interests{0}; bool registered{false}; }; void updateInterest(int fd); private: int epollFd_; std::unordered_map<int, Entry> entries_; }; #endif

3️⃣ Reactor.cpp

(保持和第三/四篇一致,这里不再删减)

👉 👉(这一段你可以直接复用前一篇代码,保持一致性)

4️⃣ Connection.h

#ifndef CONNECTION_H #define CONNECTION_H #include "Task.h" #include <string> class Reactor; class Connection { public: Connection(int fd, Reactor& reactor); ~Connection(); void start(); private: DetachedTask run(); void closeSelf(); private: int fd_; Reactor& reactor_; std::string readBuffer_; std::string writeBuffer_; }; #endif

5️⃣ Connection.cpp(核心)

#include "Connection.h" #include "Reactor.h" #include <unistd.h> #include <errno.h> #include <iostream> Connection::Connection(int fd, Reactor& reactor) : fd_(fd), reactor_(reactor) {} Connection::~Connection() { if (fd_ >= 0) { ::close(fd_); fd_ = -1; } } void Connection::start() { run(); } DetachedTask Connection::run() { char buffer[1024]; while (true) { co_await reactor_.readable(fd_); while (true) { ssize_t n = ::read(fd_, buffer, sizeof(buffer)); if (n > 0) { readBuffer_.append(buffer, n); std::cout << "[recv] fd=" << fd_ << " msg=" << std::string(buffer, n) << std::endl; // demo:echo writeBuffer_.append(buffer, n); } else if (n == 0) { std::cout << "[close] peer closed fd=" << fd_ << std::endl; closeSelf(); co_return; } else { if (errno == EAGAIN || errno == EWOULDBLOCK) { break; } std::cerr << "[error] read failed fd=" << fd_ << std::endl; closeSelf(); co_return; } } while (!writeBuffer_.empty()) { co_await reactor_.writable(fd_); ssize_t wn = ::write(fd_, writeBuffer_.data(), writeBuffer_.size()); if (wn > 0) { writeBuffer_.erase(0, wn); } else { if (errno == EAGAIN || errno == EWOULDBLOCK) { continue; } std::cerr << "[error] write failed fd=" << fd_ << std::endl; closeSelf(); co_return; } } } } void Connection::closeSelf() { if (fd_ >= 0) { reactor_.removeFd(fd_); ::close(fd_); fd_ = -1; } }

6️⃣ main.cpp

#include "ReactorThreadPool.h" #include "SocketUtil.h" #include "Connection.h" #include <sys/socket.h> #include <iostream> int main() { int listenFd = createListenFd(8080); ReactorThreadPool pool(4); pool.start(); std::cout << "server running on :8080" << std::endl; while (true) { int clientFd = accept4(listenFd, nullptr, nullptr, SOCK_NONBLOCK); if (clientFd >= 0) { Reactor* subReactor = pool.getNextReactor(); Connection* conn = new Connection(clientFd, *subReactor); conn->start(); } } return 0; }

六、执行流程(必须理解)

accept 新连接

new Connection(fd)

conn.start()

run() 协程开始

co_await readable(fd)

挂起

epoll_wait

fd ready

Reactor 恢复协程

继续执行 read/write

七、本篇核心提升(最重要)

第四篇解决:

多 Reactor + 协程(架构)

第五篇解决:

Connection + 协程(模型)


👉 一句话:

第四篇让你会“搭架构”,第五篇让你会“写框架代码”

八、总结

如果说第四篇完成的是“多 Reactor + 协程”的架构融合,那么第五篇完成的,就是“Connection + 协程”的模型融合。

从这一篇开始,连接不再只是一个 fd,也不只是一个状态容器,而是一个能够承载完整异步执行流程的服务端实体。

九、下一篇(最终篇)

👉 《C++ 服务端进阶(六)—— 工程化落地:协议、缓冲区与超时机制》


👉 你将补齐:

  • 半包 / 粘包
  • buffer 管理
  • 超时 / 心跳
  • 生命周期

函数式异步 → 对象化异步 → 工程化系统

http://www.cnnetsun.cn/news/1730050.html

相关文章:

  • 从一次炸机事故看懂示波器地线:隔离变压器、差分探头到底怎么选?
  • 嵌入式GUI开发:基于GUILite的万年历实现
  • Python新年倒计时:用代码打造节日氛围的创意实践
  • 计算机毕业设计:Python滴滴出行数据智能分析平台 Django框架 可视化 数据大屏 数据分析 大数据 机器学习 深度学习(建议收藏)✅
  • 解放加密音乐:ncmdump的格式转换革新
  • STM32外设驱动:内存映射与寄存器操作详解
  • 学生党专属方案:OpenClaw+千问3.5-27B自动整理课堂笔记
  • 单片机与手机远距离通信:WiFi与4G方案详解
  • 避坑指南:在Ubuntu 22.04上为Autoware配置Docker与NVIDIA GPU支持(含代理与镜像源配置)
  • TOPMIN库:嵌入式系统中高效追踪N个最小值的轻量级方案
  • Sanitizer工具集:高效检测内存与线程问题的实战指南
  • GLM-4.1V-9B-Base解决复杂网络问题:模拟与协议分析应用
  • C语言memcpy函数原理与优化实践
  • 嵌入式开发面试题解析与实战技巧
  • Linux内核中的命名空间技术详解
  • 安卓开发者必看:解决Google Play服务报错的5种实战方法(附工具推荐)
  • QMK Toolbox:如何用这款开源工具轻松刷写机械键盘固件?
  • NsEmuTools:终极NS模拟器管理解决方案,告别繁琐配置的困扰
  • 云原生应用的可观测性最佳实践
  • 晶振负载电容与谐振电容的快速计算与选型指南
  • Transformer在CV领域的又一次‘微操’胜利:拆解CamoFormer如何用注意力掩码玩转伪装物体分割
  • AI赋能分析:让快马平台自动完成数据探索与销售预测建模
  • Cadence Allegro 16.6 环境设置保姆级指南:从绘图参数到自动保存,新手避坑必看
  • 在普通硬件上实现实时AI语音交互的技术突破:Neuro开源项目的边缘计算实践
  • Android音视频开发实战:MediaCodec同步解码避坑指南(附PTS矫正技巧)
  • Typora 添加锚点实现文档内部快速跳转
  • Notion Enhancer:给你的Notion装上“超能力“的魔法工具箱
  • TongRDS多主多从集群部署实战:从配置到验证的完整指南
  • HJ165 小红的优惠券
  • League Akari:基于LCU API的模块化游戏自动化框架深度解析