


目录
[一 :😀Reactor 模式](#一 :😀Reactor 模式)
[1-1 🍕 Reactor 模式的核心思想](#1-1 🍕 Reactor 模式的核心思想)
[1-2 🍔 Reactor 模式的组成](#1-2 🍔 Reactor 模式的组成)
[二 :😁案例使用 -- 基于Reactor的计算器](#二 :😁案例使用 – 基于Reactor的计算器)
[2-1🍟 封装 epoll](#2-1🍟 封装 epoll)
[2-2🌭 封装 Connection](#2-2🌭 封装 Connection)
[2-3 🍿 封装 Reactor](#2-3 🍿 封装 Reactor)
[2-4🧂 封装 Listener(专门负责获取连接的模块)](#2-4🧂 封装 Listener(专门负责获取连接的模块))
[2-5🥓 封装 IOService(只负责IO的)](#2-5🥓 封装 IOService(只负责IO的))
[2-6 🥚 Main.cc](#2-6 🥚 Main.cc)
[三 :😂 复盘分析](#三 :😂 复盘分析)
[3-1🍳 多路转接对写的处理](#3-1🍳 多路转接对写的处理)
[3-2🧇 最佳实践 One Thread One loop](#3-2🧇 最佳实践 One Thread One loop)
前言
- 在高并发IO场景下,传统的阻塞IO模型因线程资源浪费、响应效率低下等问题,已难以满足系统性能需求。Reactor模式作为一种经典的事件驱动架构模式,通过多路复用技术实现了单线程(或少量线程)对多IO事件的高效管理,成为解决高并发IO问题的核心方案之一。无论是网络通信框架、服务器开发还是高性能工具构建,Reactor模式都有着广泛的应用。
- 本文旨在从理论到实践,系统解析Reactor模式的核心逻辑与工程实现。
一 :😀Reactor 模式
🐳 Reactor 模式 是一种事件处理设计模式,用于处理多个并发输入事件。它通过事件驱动的方式,将事件分发给相应的处理程序,从而实现对并发事件的高效处理。Reactor 模式广泛应用于网络编程、服务器框架等领域,例如 Java 的 NIO、Netty 框架,以及 C++ 的 Boost.Asio 等。
1-1 🍕 Reactor 模式的核心思想
Reactor 模式的核心思想是:
- 事件驱动:通过事件循环( Event Loop )监听多个事件源(多个文件描述符 )(如 Socket、文件描述符等),该事件驱动器可以采用 select,poll,epoll 等。
- 事件分发:当事件发生时,Reactor 将事件分发给对应的事件处理器( Event Handler )。
- 非阻塞:Reactor 模式通常与 非阻塞 I/O 结合使用,避免线程阻塞,所以需要将 Socket、文件描述符等通过 fcntl 函数设置为非阻塞状态。
1-2 🍔 Reactor 模式的组成
Reactor 模式通常由以下几个组件组成:
1️⃣Loop(反应器 / 事件循环)
- 负责监听事件源(多个文件描述符)(如 Socket、文件描述符等) 。
- 当事件发生时,将事件分发给对应的事件处理器。
2️⃣Dispatcher(事件多路分发器)
- 当事件发生时,通知 Event Handler 进行处理 。
3️⃣Event Handler(事件处理器)
- 定义处理事件的接口 。
- 每个事件源对应一个事件处理器 。
二 :😁案例使用 -- 基于Reactor的计算器
2-1🍟 封装 epoll
cpp#pragma once #include <iostream> #include <sys/epoll.h> #include "Log.hpp" #include "Common.hpp" using namespace LogModule; namespace EpollMoudle { class Epoller { public: Epoller() : _epfd(-1) {} void Init() { _epfd = epoll_create(256); if(_epfd < 0) { LOG(LogLevel::ERROR) << "epoll_create error"; exit(EPOLL_CREATE_ERR); } LOG(LogLevel::INFO) << "epoll_create success, epfd: " << _epfd; } int Wait(struct epoll_event revs[], int num, int timeout) // 输出就绪的fd 和 events { int n = epoll_wait(_epfd, revs, num, timeout); if(n < 0) { LOG(LogLevel::WARNING) << "epoll_wait error"; } return n; } void Ctrl(int sockfd, uint32_t events, int flag) { struct epoll_event ev; ev.events = events; ev.data.fd = sockfd; int n = epoll_ctl(_epfd, flag, sockfd, &ev); if (n < 0) { LOG(LogLevel::WARNING) << "epoll_ctl error"; } } void Add(int sockfd, uint32_t events) { Ctrl(sockfd, events, EPOLL_CTL_ADD); } void Update(int sockfd, uint32_t events) { Ctrl(sockfd, events, EPOLL_CTL_MOD); } void Delete(int sockfd) { int n = epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr); if (n < 0) { LOG(LogLevel::WARNING) << "epoll_ctl error"; } } ~Epoller() {} private: int _epfd; }; }
2-2🌭 封装 Connection
所有的连接里都包含回指指针 指向自己的 Reactor 模型
cpp#pragma once #include <iostream> #include <string> #include <memory> #include <functional> #include "InetAddr.hpp" class Reactor; // 循环依赖的问题 // 普通fd,Listensockfd // 让对 fd 的处理方式采用同一种方式 // 描述一个连接 class Connection { public: Connection() : _sockfd(-1), _events(0) { // 自动获得当前系统的时间戳 } void UpdateTime() { // 更新时间, 重新获取时间 } void SetPeerInfo(const InetAddr &peer_addr) { _peer_addr = peer_addr; } void SetSockfd(int sockfd) { _sockfd = sockfd; } int Sockfd() { return _sockfd; } void SetEvents(uint32_t events) { _events = events; } uint32_t GetEvents() { return _events; } void SetOwner(Reactor *owner) { _owner = owner; } Reactor* GetOwner() { return _owner; } void Append(const std::string &in) // 把收到的数据添加到自己的接受缓冲区 { _inbuffer += in; } void AppendToOut(const std::string &out) { _outbuffer += out; } void DiscardOutString(int n) { _outbuffer.erase(0, n); } bool isOutBufferEmpty() { return _outbuffer.empty(); } std::string &OutString() { return _outbuffer; } std::string &InBuffer() // 故意 { return _inbuffer; } void Close() { if(_sockfd >= 0) close(_sockfd); } // 回调方法 virtual void Sender() = 0; virtual void Recver() = 0; virtual void Excepter() = 0; ~Connection() { } private: int _sockfd; // 每一个文件描述符都有自己的输入输出缓冲区 std::string _inbuffer; std::string _outbuffer; InetAddr _peer_addr; // 对应哪一个客户端 // 添加一个指针 Reactor *_owner; // 我关心的事件 uint32_t _events; // 我这个 connection 关心的事件 // lastmodtime uint64_t _timestamp; };
2-3 🍿 封装 Reactor
cpp#pragma once #include <iostream> #include <memory> #include <unordered_map> #include "Epoller.hpp" #include "Connection.hpp" using namespace EpollMoudle; using connection_t = std::shared_ptr<Connection>; class Reactor { const static int event_num = 64; private: bool IsConnectionExists(int sockfd) { return _connections.find(sockfd) == _connections.end() ? false : true; } public: Reactor() : _isrunning(false), _epoller(std::make_unique<Epoller>()) { _epoller->Init(); } void InsertConnection(connection_t conn) { auto iter = _connections.find(conn->Sockfd()); if (iter == _connections.end()) { // 1. 把连接,放到unordered_map中进行管理 _connections.insert(std::make_pair(conn->Sockfd(), conn)); // 2. 把新插入进来的连接,写透到内核的epoll中 _epoller->Add(conn->Sockfd(), conn->GetEvents()); // 3. 设置关联关系,让connection回指当前对象 conn->SetOwner(this); } } void EnableReadWrite(int sockfd, bool readable, bool writeable) { if(IsConnectionExists(sockfd)) { // 修改用户层connection的事件 uint32_t events = ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET); _connections[sockfd]->SetEvents(events); // 写透到内核中 _epoller->Update(sockfd, _connections[sockfd]->GetEvents()); } } void DelConnection(int sockfd) { if(IsConnectionExists(sockfd)) { // 1. 从内核中移除对sockfd的关心 _epoller->Delete(sockfd); // 2. 关闭特定的文件描述符 _connections[sockfd]->Close(); // 3. 从_connectionns中移除对应的connection _connections.erase(sockfd); } } // 基于事件驱动的事件派发器 void Dispatcher(int n) { for (int i = 0; i < n; i++) { // 开始进行派发, 派发给指定的模块 int sockfd = _revs[i].data.fd; uint32_t revents = _revs[i].events; if ((revents & EPOLLERR) || (revents & EPOLLHUP)) revents = (EPOLLIN | EPOLLOUT); // 异常事件,转换成为读写事件 if ((revents & EPOLLIN) && IsConnectionExists(sockfd)) { _connections[sockfd]->Recver(); } if ((revents & EPOLLOUT) && IsConnectionExists(sockfd)) { _connections[sockfd]->Sender(); } } } void LoopOnce(int timeout) { int n = _epoller->Wait(_revs, event_num, timeout); Dispatcher(n); } void DebugPrint() { std::cout << "Epoller 管理的fd: "; for(auto &iter : _connections) { std::cout << iter.first << " "; } std::cout << std::endl; } void Loop() { _isrunning = true; // int timeout = -1; int timeout = 1000; while (_isrunning) { LoopOnce(timeout); DebugPrint(); // 超时管理 // 简单的,遍历_connections 判断当前时间 - connection 的最近访问时间 > XXX // 超时了 } _isrunning = false; } void Stop() { _isrunning = false; } ~Reactor() { } private: std::unique_ptr<Epoller> _epoller; std::unordered_map<int, connection_t> _connections; // fd: Connection 服务器内部所有的连接 bool _isrunning; struct epoll_event _revs[event_num]; };
2-4🧂 封装 Listener(专门负责获取连接的模块)
cpp#pragma once #include <iostream> #include <memory> #include "Socket.hpp" #include "InetAddr.hpp" #include "Connection.hpp" #include "Epoller.hpp" #include "Log.hpp" #include "IOService.hpp" #include "Reactor.hpp" #include "Protocol.hpp" #include "Calculator.hpp" using namespace SocketModule; using namespace LogModule; // 专门负责获取连接的模块 // 连接管理器 class Listener : public Connection { public: Listener(uint16_t port) : _listensock(std::make_unique<TcpSocket>()), _port(port) { _listensock->BuildTcpSocketMethod(_port); SetSockfd(_listensock->Fd()); SetEvents(EPOLLIN | EPOLLET); } virtual void Sender() override { } // 我们回调到这里 天然就有父类connection virtual void Recver() override { // 读就绪,而且是listensock就绪 // IO处理 --- 获取新连接 // 你怎么知道 一次来的就是一个连接呢 怎么保证一次读完了 ET工作模式 ! while(true) { InetAddr peer; int aerrno = 0; // accept 非阻塞的时候,就是IO,我们就像处理read一样,处理accept int sockfd = _listensock->Accepter(&peer, &aerrno); if(sockfd > 0) { // success // 不能直接读取 ! // sockfd 添加到epoll ! // epollserver只认connection LOG(LogLevel::DEBUG) << "Accepter success: " << sockfd; // 普通的文件描述符, 处理IO的 也是connection // 2. sockfd包装成为 Connection ! auto conn = std::make_shared<IOService>(sockfd); conn->RegisterOnMessage(HandlerRequest); // 3. 插入到EpollServer GetOwner()->InsertConnection(conn); } else { if(aerrno == EAGAIN || aerrno == EWOULDBLOCK) { LOG(LogLevel::DEBUG) << "accepter all connection ... done"; break; } else if(aerrno == EINTR) { LOG(LogLevel::DEBUG) << "accepter intr by signal, continue"; continue; } else { LOG(LogLevel::DEBUG) << "accepter error ... Ignore"; break; } } } } virtual void Excepter() override { } int Sockfd() { return _listensock->Fd(); } ~Listener() { _listensock->Close(); } private: std::unique_ptr<Socket> _listensock; uint16_t _port; };
2-5🥓 封装 IOService(只负责IO的)
cpp#pragma once #include <iostream> #include <memory> #include <functional> #include "Socket.hpp" #include "InetAddr.hpp" #include "Connection.hpp" #include "Epoller.hpp" #include "Log.hpp" #include "Reactor.hpp" using func_t = std::function<std::string(std::string &)>; using namespace SocketModule; using namespace LogModule; // 只负责IO class IOService : public Connection { static const int size = 1024; public: IOService(int sockfd) { // 1. 设置文件描述符非阻塞 SetNonBlock(sockfd); SetSockfd(sockfd); SetEvents(EPOLLIN | EPOLLET); } virtual void Sender() override { // UpdateTime(); // 直接写 while(true) { ssize_t n = send(Sockfd(), OutString().c_str(), OutString().size(), 0); if(n > 0) { // 成功 DiscardOutString(n); // 移除 N个 } else if(n == 0) { break; } else { if(errno == EAGAIN || errno == EWOULDBLOCK) { // 缓冲区写满了,下次再来 break; } else if(errno == EINTR) { continue; } else { Excepter(); return ; } } } // 一种:outbuffer empty // 一种:发送缓冲区写满了 && outbuffer没有empty 写条件不满足 使能 sockfd 在 epoll 中的事件 if(!isOutBufferEmpty()) { // 修改对sockfd 的读事件关心! -- 开启对写事件关心 // 按需设置! GetOwner()->EnableReadWrite(Sockfd(), true, true); // 读事件一般常设 写事件一般按需设置 } else { GetOwner()->EnableReadWrite(Sockfd(), true, false); // 按需设置 关闭了 !!! } } virtual void Recver() override { // UpdateTime(); // 1. 读取所有数据 while(true) // ET模式 { char buffer[size]; ssize_t s = recv(Sockfd(), buffer, sizeof(buffer) - 1, 0); // 非阻塞 if(s > 0) { buffer[s] = 0; // 读取成功 Append(buffer); } else if(s == 0) { // 对端关闭连接 Excepter(); return ; } else { if(errno == EAGAIN || errno == EWOULDBLOCK) { break; } else if(errno == EINTR) { continue; } else { // 发生错误了 Excepter(); return ; } } } // 走到下面,我一定把本轮数据读完了 std::cout << "outbuffer: \n" << InBuffer() << std::endl; // 你能确保你读到的消息,就是一个完整的报文吗 // 我怎么知道 读到了完整的请求呢?? 协议 ! ! ! std::string result; if(_on_message) result = _on_message(InBuffer()); // 添加应答信息 AppendToOut(result); // 如何处理写的问题 outbuffer 发送给对方的问题 if(!isOutBufferEmpty()) { // 方案一: Sender(); // 直接发送,推荐做法 // Sender(); // 方案二: 使能writeable即可 GetOwner()->EnableReadWrite(Sockfd(), true, true); } } virtual void Excepter() override { // IO读取的时候,所有的异常处理,全部都会转化成为这一个函数的调用 // 出异常 怎么做? // 打印日志 差错处理 关闭连接, Reactor异常connection,从内核中,移除对fd的关心 LOG(LogLevel::INFO) << "客户端连接可能结束,进行异常处理: " << Sockfd(); GetOwner()->DelConnection(Sockfd()); } void RegisterOnMessage(func_t on_message) { _on_message = on_message; } ~IOService() {} private: func_t _on_message; };
2-6 🥚 Main.cc
cpp#include <iostream> #include <string> #include "Log.hpp" #include "Listener.hpp" #include "Connection.hpp" #include "Reactor.hpp" using namespace LogModule; int main(int argc, char *argv[]) { ENABLE_CONSOLE_LOG(); if (argc != 2) { std::cout << "Usage: " << argv[0] << " port" << std::endl; return 0; } uint16_t local_port = std::stoi(argv[1]); Reactor reactor; auto conn = std::make_shared<Listener>(local_port); reactor.InsertConnection(conn); reactor.Loop(); return 0; }
三 :😂 复盘分析
3-1🍳 多路转接对写的处理
1️⃣写事件是否就绪:发送缓冲器,是否有空间 !
有空间。sockfd,发送缓冲区中的空间,默认就是有的!!!,只有当他被填写满了,写条件才不具备
2️⃣如何正确处理写入:直接写(不会被阻塞),写缓冲区可能会越来越短,写满了,写条件不具备,才托管给 epoll ,让它帮我关心 !!!
- 直接写!写入默认就是就绪的 !
- 写入失败,才托管给 epoll !
- 多路转接的方案设计的时候,写事件关心,永远不能常开启。
- 写事件关心 , 按需设置 !
- 读事件一般常设,写事件一般按需设置 !
- 只要使能开启写,内核+epoll 会自动事件派发处理底层的发送任务。
- 从此往后,我们专注于业务即可 !
3-2🧇 最佳实践 One Thread One loop
🦈 一个 fd (Connection) 的全生命周期,只能由一个线程统一管理,这样就不会存在任何(过多)并发问题
🧊一个loop就是 一个Reactor****
🔥提炼与总结🔥
- 本文通过"理论解析-案例实现-复盘提炼"的逻辑,完成了对Reactor模式的全方位解读,核心内容与实践启示可总结为以下三点:
- 1️⃣Reactor模式的核心价值在于"事件驱动+多路复用"的组合拳。其核心思想是通过一个"反应器"(Reactor)作为事件调度中心,借助epoll等多路转接技术监听多个IO事件,当事件触发时再分发至对应的处理逻辑,从根本上解决了传统阻塞IO"一连接一线程"的资源浪费问题。模式的组成部分相互协作:Reactor负责事件监听与分发,Handler负责具体事件处理,多路复用器提供IO事件监控能力,三者共同构成了高效的事件处理闭环。
- 2️⃣工程实践中需注重模块的分层封装与职责单一。基于Reactor的计算器案例中,我们将核心能力拆分为epoll封装(提供多路复用基础)、Connection封装(管理连接与数据)、Reactor封装(事件调度核心)、Listener封装(连接接入)、IOService封装(IO处理)等模块,每个模块仅承担单一职责。这种封装方式不仅提升了代码的可维护性与可复用性,更让Reactor模式的核心逻辑在工程中清晰落地,避免了因职责混乱导致的扩展性问题。
- 3️⃣模式应用需关注关键细节与最佳实践。复盘分析显示,多路转接技术对写事件的处理需谨慎:因写缓冲区未满时写事件会持续触发,需结合"数据是否写完"的状态判断避免无效回调;而"One Thread One Loop"作为Reactor模式的最佳实践,通过单个线程管理一个事件循环与对应的IO资源,避免了线程切换开销与锁竞争,大幅提升了单线程IO处理效率,是高性能场景下的优选方案。
- 综上,Reactor模式并非复杂的理论概念,而是一套可落地的高并发IO解决方案。掌握其核心思想、理解模块协作逻辑,并结合工程实践中的封装技巧与最佳实践,才能真正发挥其在高性能系统开发中的核心价值。
结束语
以上是我对于【Linux网络编程】「高性能网络模式:Reactor 反应堆模式」的理解
感谢您的三连支持!!!




