网络编程(5)—— Reactor实现(v1)

发布时间:2026/10/6 22:10:01

网络编程(5)—— Reactor实现(v1) 文章目录回顾Reactor_v1实现Socket类实现头文件源文件InetAddr类的实现头文件源文件Acceptor类的实现头文件源文件SocketIO类的实现头文件源文件TcpConnection类的实现头文件源文件回顾先回顾一下我们整个的实现框架所以第一步应该是实现的是——封装系统调用另外为了方便打印调试信息可以把日志系统用单例模式先实现出来。关于用单例模式实现日志系统我们在之前的文章中已经实现过了这里就不过多赘述了。详情可以跳转到该文章中查看Reactor网络模型本质上就是使用epoll的并发网络模型通过连接器连接到服务器网络然后注册三个事件(对应epoll的三个接口函数)通过事件驱动(事件循环和事件分发)来响应客户端的请求。Reactor_v1实现在写代码之前要整理一下思绪框架图中已经很明确的写出了有哪些类我们在第一个版本的内容中实际上就是对系统调用的封装下面就画一个类图整理一下具体实现的内容。Acceptor类中可以将地址复用、端口复用、bind、listen这些写法固定的流程封装在一个ready函数中。关于其它类行该可以比较容易的理清思路。需要着重考虑的是IO操作因为使用send/recv或write/read一次处理的字符数量是由上限的。所以我们应该设置一个合适大小的缓冲用来接收数据流。Socket类实现头文件/** * 封装文件描述符 */#ifndef_SOCKET_H#define_SOCKET_H#includesys/types.h#includesys/socket.hclassSocket{public:Socket();/** * param fd * 禁止隐式转换防止出现问题 */explicitSocket(intfd);~Socket();intgetFd();private:int_fd;};#endif//_SOCKET_H源文件/** * 封装文件描述符 */#includeSocket.h#includestdio.h#includestdlib.h/** * Socket implementation */Socket::Socket(){_fd::socket(AF_INET,SOCK_STREAM,0);if(_fd-1){perror(socket error);exit(EXIT_FAILURE);}}/** * param fd */Socket::Socket(intfd):_fd(fd){}Socket::~Socket(){}/** * return int */intSocket::getFd(){return_fd;}InetAddr类的实现头文件/** * 地址相关操作 */#ifndef_INETADDRESS_H#define_INETADDRESS_H#includestring#includenetinet/in.h#includenetinet/ip.h#includesys/socket.h#includearpa/inet.husingstd::string;classInetAddress{public:/** * param ip * param port */InetAddress(conststringip,unsignedshortport);/** * param addr */InetAddress(conststructsockaddr_inaddr);~InetAddress();stringgetIp();unsignedshortgetPort();conststructsockaddr_in*getInetAddressPtr();private:structsockaddr_in_addr;};#endif//_INETADDRESS_H源文件/** * 地址相关操作 */#includeInetAddress.h/** * InetAddress implementation *//** * param ip * param port */InetAddress::InetAddress(conststringip,unsignedshortport){_addr.sin_familyAF_INET;_addr.sin_porthtons(port);_addr.sin_addr.s_addr::inet_addr(ip.c_str());}/** * param addr */InetAddress::InetAddress(conststructsockaddr_inaddr):_addr(addr){}InetAddress::~InetAddress(){}/** * return string */stringInetAddress::getIp(){string temp::inet_ntoa(_addr.sin_addr);returntemp;}/** * return unsigned short */unsignedshortInetAddress::getPort(){return::ntohs(_addr.sin_port);}/** * return const struct sockaddr_in * */conststructsockaddr_in*InetAddress::getInetAddressPtr(){return_addr;}Acceptor类的实现头文件/** * 连接器的实现 */#ifndef_ACCEPTOR_H#define_ACCEPTOR_H#includeInetAddress.h#includeSocket.husingstd::string;classAcceptor{public:/** * param ip * param port */Acceptor(conststringip,unsignedshortport);~Acceptor();voidready();intaccept();private:Socket _sock;InetAddress _addr;voidsetReuseAddr();voidsetReusePort();voidbind();voidlisten();};#endif//_ACCEPTOR_H源文件/** * 连接器的实现 */#includeAcceptor.h/** * Acceptor implementation *//** * param ip * param port */Acceptor::Acceptor(conststringip,unsignedshortport):_sock(),_addr(ip,port){}Acceptor::~Acceptor(){}/** * return void */voidAcceptor::ready(){setReuseAddr();setReusePort();bind();listen();return;}/** * return int */intAcceptor::accept(){intclientfd::accept(_sock.getFd(),NULL,NULL);if(clientfd-1){perror(accept error);return-1;}returnclientfd;}/** * return void */voidAcceptor::setReuseAddr(){intopt1;if(::setsockopt(_sock.getFd(),SOL_SOCKET,SO_REUSEADDR,opt,sizeof(opt))-1){perror(reuse sockaddr error);exit(EXIT_FAILURE);}return;}/** * return void */voidAcceptor::setReusePort(){intopt1;if(::setsockopt(_sock.getFd(),SOL_SOCKET,SO_REUSEPORT,opt,sizeof(opt))-1){perror(reuse port error);exit(EXIT_FAILURE);}return;}/** * return void */voidAcceptor::bind(){if(::bind(_sock.getFd(),(structsockaddr*)_addr.getInetAddressPtr(),sizeof(structsockaddr))-1){perror(bind error);exit(EXIT_FAILURE);}return;}/** * return void */voidAcceptor::listen(){if(::listen(_sock.getFd(),100)-1){perror(listen error);exit(EXIT_FAILURE);}return;}SocketIO类的实现头文件/** * IO操作 */#ifndef_SOCKETIO_H#define_SOCKETIO_H#includeunistd.h#includeerrno.h#includestdlib.h#includestdio.h#includesys/socket.h#includesys/types.hclassSocketIO{public:/** * param fd */explicitSocketIO(intfd);~SocketIO();/** * param buf * param len */intreadn(char*buf,intlen);/** * param buf * param len */intreadLine(char*buf,intlen);/** * param buf * param len */intwriten(constchar*buf,intlen);private:int_fd;};#endif//_SOCKETIO_H源文件/** * IO操作 */#includeSocketIO.h/** * SocketIO implementation *//** * param fd */SocketIO::SocketIO(intfd):_fd(fd){}SocketIO::~SocketIO(){if(_fd!-1){close(_fd);_fd-1;}}/** * param buf * param len * return int */intSocketIO::readn(char*buf,intlen){intremainlen;// 剩余没读的字节intret0;char*pstrbuf;while(remain0){ret::read(_fd,pstr,sizeof(remain));if(ret-1errnoEINTR){::perror(read error);return-1;}elseif(ret0){break;}else{pstrret;remain-ret;}}returnlen-remain;}/** * param buf * param len * return int */intSocketIO::readLine(char*buf,intlen){intremainlen-1;intret0,total0;char*pstrbuf;while(remain0){ret::recv(_fd,pstr,remain,MSG_PEEK);if(ret-1errnoEINTR){::perror(readLine error);return-1;}elseif(ret0){break;}else{for(inti0;iret;i){if(pstr[i]\n){intszi1;readn(pstr,sz);pstrsz;*pstr\0;returnsztotal;}}readn(pstr,ret);totalret;pstrret;remain-ret;}}*pstr\0;returntotal;}/** * param buf * param len * return int */intSocketIO::writen(constchar*buf,intlen){intremainlen;constchar*pstrbuf;intret0;while(remain0){retwrite(_fd,pstr,remain);if(ret-1errnoEINTR){::perror(writen error);return-1;}elseif(ret0){break;}else{pstrret;remain-ret;}}returnlen-remain;}TcpConnection类的实现头文件/** * 连接管理 */#ifndef_TCPCONNECTION_H#define_TCPCONNECTION_H#includestring#includeSocketIO.husingstd::string;classTcpConnection{public:/** * param fd */explicitTcpConnection(intfd);~TcpConnection();stringreceive();/** * param msg */voidsend(conststringmsg);private:SocketIO _sockIO;};#endif//_TCPCONNECTION_H源文件/** * 连接管理 */#includeTcpConnection.h/** * TcpConnection implementation *//** * param fd */TcpConnection::TcpConnection(intfd):_sockIO(fd){}TcpConnection::~TcpConnection(){}/** * return string */stringTcpConnection::receive(){charbuffer[1024]{0};_sockIO.readLine(buffer,sizeof(buffer));returnstring(buffer);}/** * param msg * return void */voidTcpConnection::send(conststringmsg){_sockIO.writen(msg.c_str(),msg.size());return;}以上就是Reactor的全部函数完成了对系统调用的封装。下面再写一个测试文件#includeAcceptor.h#includeTcpConnection.h#includeiostreamusingstd::cout;usingstd::endl;voidtest0(){Acceptoracceptor(0.0.0.0,8080);acceptor.ready();TcpConnectioncon(acceptor.accept());while(1){/* cout recv msg from client: con.receive() endl; *//* con.send(hello,world\n); */string msgcon.receive();coutrecv msg from client: msgendl;con.send(msg:msg);}}intmain(){test0();return0;}下面再开一个终端自测一下基本上还是符合我们预期的也就是说明我们对于底层的封装没有了太大问题。我们的Reactor第一代基本完成了当然可以把之前封装的log4cpp的代码替代perror()。下面的工作就是完成核心层事件驱动的代码就是对epoll的封装另外TcpConnection是用来管理连接的所以当我们使用服务器采用事件驱动的形式时TcpConnection中的数据成员和成员函数也要随之变动。
延伸阅读

更多相关文章

2026/10/2 16:04:15

ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor实现ScheduledExecutorService接口&#xff0c;实现了一些定时任务处理的方法。public interface ScheduledExecutorService extends ExecutorService {ScheduledFuture<?> schedule(Runnable var1, long var2, TimeUnit var4);<V> Sc…

2026/10/6 1:18:54

PDMS多部门共用许可证时,企业怎么建立统一的调配机制

很多企业在做工业软件许可证管理时&#xff0c;都会遇到一种很典型的情况&#xff1a;一边看到许可证利用率不高&#xff0c;一边又持续感受到资源紧张和并发冲突。表面上看&#xff0c;这像是一个矛盾现象&#xff1b;但从许可证监控和使用分析的角度看&#xff0c;这恰恰说明…

2026/10/6 22:09:49

单周期MIPS CPU设计:Logisim实战与控制信号精解

1. 为什么单周期MIPS CPU是数字电路学习的“分水岭”Logisim里拖几个门电路、连几根线&#xff0c;做个加法器或计数器——这叫入门&#xff1b;但当你第一次把取指、译码、执行、访存、写回这五个阶段&#xff0c;用真实信号线串成一条能跑通add $t0, $t1, $t2指令的完整通路&…

2026/10/6 22:09:49

LangSmith实战:搭建RAG量化评估体系,告别“感觉还行”

做过RAG项目的朋友应该都有过这种体验&#xff1a;本地跑demo的时候&#xff0c;随手扔几个问题进去&#xff0c;回答像模像样&#xff0c;心里想着"这玩意儿稳了"。结果一上线&#xff0c;真实用户的问题五花八门&#xff0c;同一个知识点换个问法就答得牛头不对马嘴…

2026/10/6 22:09:49

SSE流式到结构化输出:LangChain解析器与ToolCall实战

开头先说实话&#xff1a;干 LangChain 应用最烦的不是让模型说对一句话&#xff0c;而是怎么让这句话以一种“前端看得懂、后端不炸锅”的形式到达用户手里。SSE 流式、OutputParser、ToolCall 这三个词&#xff0c;几乎每个接近生产的 LangChain 项目都会碰到。标题写的是“从…

2026/10/6 22:09:48

大模型自动化评测平台:AllData+Coze-Loop实战架构

1. 项目概述&#xff1a;一个真正能跑起来的大模型评测平台长什么样&#xff1f;“AllData集成开源项目Coze-Loop&#xff0c;建设大模型评测平台&#xff0c;实现模型自动化测评、效果量化评估&#xff0c;助力 AI 应用落地&#xff01;”——这句话不是PPT里的口号&#xff0…

2026/10/6 22:04:48

浏览器Agent插件实战:3分钟解放双手的自动化方案

1. 浏览器Agent插件到底解决了什么痛点浏览器自动化这件事&#xff0c;做了十几年了。从最早的Selenium写脚本&#xff0c;到后来的Puppeteer、Playwright&#xff0c;工具一直在进化&#xff0c;但核心矛盾始终没变&#xff1a;写规则的人永远追不上网页变化的速度。你花两天写…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起&#xff1a;为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高&#xff0c;很多人第一次听到会以为是某个新模型的名字&#xff0c;其实它更像是一种思路——把Jev模型的能力当作底座&#xff0c;通过Agent的方式去接管浏览器&#xf…

2026/10/6 4:01:51

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同"&#xff1a;多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西&#xff0c;大概率会有一种感觉&#xff1a;单个 Agent 能做的事情&#xff0c;其实很快就摸到天花板了。你给它一个提示词&#xff0c;挂几个工…

2026/10/6 17:46:51

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 0:03:23

MR25H40CDF+STM32F031C6工业级高可靠数据存储方案

1. 项目概述&#xff1a;为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储&#xff1f;在工厂产线的 PLC 控制柜里、在风电变流器的散热片背面、在矿井监测终端的金属外壳下&#xff0c;你经常能看到一块指甲盖大小的黑色芯片——它既不是 Flash&#xff0c;也不是…

2026/10/6 0:03:23

MRAM+STM32工业断电数据保全实战指南

1. 项目概述&#xff1a;为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储&#xff1f;在工厂产线的PLC柜里、在野外无人值守的环境监测终端里、在高速运转的包装机控制板上&#xff0c;你经常能看到一块指甲盖大小的黑色芯片&#xff0c;旁边贴着“MR25H40CDF”丝…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑