高级IO_多路转接之ET模式Reactor

文章目录

  • Reactor是什么?
    • LT模式 VS ET模式
  • 示例代码


提示:以下是本篇文章正文内容,下面案例可供参考

Reactor是什么?

Reactor模式是一种事件驱动的并发模型,它通过将事件处理逻辑与事件分发机制解耦,实现高性能、可扩展的并发处理。Reactor模式适用于大量短时连接或需要高效I/O处理的场景,如Web服务器、聊天服务器等。

今天我们所实现的Reactor是基于ET模式下的多路转接模式。

LT模式 VS ET模式

以我们的epoll为例,我们的epoll默认是LT模式。
LT模式:只要有事件就绪,就会不断提醒。
ET模式:只有事件从无到有,从少到多的情况,才会提醒一次

所以对于ET模式而言,就需要逼服务器一次性将所有缓冲区数据全部读完,也算是逼着你效率提高。 而只提醒一次也是高效的表现。

示例代码

#include "Epoll.hpp"
#include "Socket.hpp"
#include <vector>
#include <functional>
#include <unordered_map>
#include "Common.hpp"
#include "Calculator.hpp"class Connection;
class ReactorServer;using func_t = std::function<void(std::shared_ptr<Connection>)>;
const std::string default_ip = "0.0.0.0";
const uint16_t default_port = 8080;#define EVENT_IN (EPOLLIN | EPOLLET)
#define EVENT_OUT (EPOLLOUT | EPOLLET)void SetNonBlock(int fd)
{int fl = fcntl(fd, F_GETFL);if (fl < 0){// 获取失败perror("F_GETFD Error");return;}int n = fcntl(fd, F_SETFL, fl | O_NONBLOCK);if (n < 0){perror("Set Nonblock Error");}else{lg(Info, "Set Nonblock Succeed, Fd: %d", fd);// std::cout << "Fd:" << fd << " ,set nonblock done" << std::endl;}
}class Connection : public nocopy
{
public:Connection(int sockfd, ReactorServer *reactor_server): _sockfd(sockfd), _reactor_server(reactor_server) {}void SetHandle(func_t recv_cb, func_t send_cb, func_t except_cb){_recv_cb = recv_cb;_send_cb = send_cb;_except_cb = except_cb;}int Getfd(){return _sockfd;}std::string &GetInBuffer(){return _inbuffer;}std::string &GetOutBuffer(){return _outbuffer;}~Connection() {}private:int _sockfd;std::string _inbuffer;std::string _outbuffer;ReactorServer *_reactor_server;public:func_t _recv_cb;func_t _send_cb;func_t _except_cb;
};class ReactorServer
{
private:const int num = 128;public:ReactorServer(uint16_t port, func_t handle_message): _port(port), _listensock(new Socket), _epoller(new Epoller), _handle_message(handle_message) {}void EnableEvent(int fd, bool recv, bool send){int events = 0;events |= (recv ? EVENT_IN : 0);events |= (send ? EVENT_OUT : 0);_epoller->EpollerUpdate(EPOLL_CTL_MOD, fd, events);}void Accepter(std::shared_ptr<Connection> connection){while (true){int newsock = _listensock->Accept();if (newsock < 0){if (errno == EWOULDBLOCK){break;}else if (errno == EINTR){continue;}lg(Warning, "Accept Error...");break;}AddConnetion(newsock, EVENT_IN, std::bind(&ReactorServer::Recver, this, std::placeholders::_1),std::bind(&ReactorServer::Sender, this, std::placeholders::_1),std::bind(&ReactorServer::Excepter, this, std::placeholders::_1));}}void Recver(std::shared_ptr<Connection> connection){int fd = connection->Getfd();while (true){char buffer[1024];memset(buffer, 0, sizeof buffer);int n = recv(connection->Getfd(), buffer, sizeof buffer - 1, 0);if (n > 0){connection->GetInBuffer() += buffer;std::cout << connection->GetInBuffer();}else if (n < 0){if (errno == EWOULDBLOCK)break;else if (errno == EINTR)continue;lg(Warning, "Read Error...");connection->_except_cb(connection);return;}else{lg(Info, "Foreign Host Closed...");connection->_except_cb(connection);return;}}_handle_message(connection);}void Sender(std::shared_ptr<Connection> connection){int fd = connection->Getfd();std::string &mes = connection->GetOutBuffer();while (1){int n = send(fd, mes.c_str(), mes.size(), 0);if (n < 0){if (errno == EWOULDBLOCK){break;}else if (errno == EINTR){continue;}connection->_except_cb(connection);return;}if (n == 0){break;}mes.erase(0, n);if (mes.empty()){break;}}if (!mes.empty()){EnableEvent(fd, true, true);}else{EnableEvent(fd, true, false);}}void Excepter(std::shared_ptr<Connection> connection){int fd = connection->Getfd();// 1.先从内核中移除_epoller->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);// 2.从_connections中移除_connections.erase(fd);// 3.关闭sockfdclose(fd);lg(Info, "Sockfd: %d Closed...", fd);}void AddConnetion(int fd, int events, func_t recv_cb, func_t send_cb, func_t except_cb){// 1.设置非阻塞SetNonBlock(fd);// 2.创建新connectionstd::shared_ptr<Connection> newcon(new Connection(fd, this));newcon->SetHandle(recv_cb, send_cb, except_cb);// 3.插入到_connetcions_connections[fd] = newcon;// 4.放入内核_epoller->EpollerUpdate(EPOLL_CTL_ADD, fd, events);}void Init(){_epoller->Init();_listensock->Init();_listensock->Bind(AF_INET, default_ip, _port);_listensock->Listen();AddConnetion(_listensock->_sockfd, EVENT_IN, std::bind(&ReactorServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);}bool IsConnectionSafe(int fd){return _connections.find(fd) == _connections.end() ? false : true;}void Start(){struct epoll_event recvs[num];while (1){int n = _epoller->EpollWait(recvs, num, -1);if (n > 0){// std::cout << "检测到事件 n:" << n << std::endl;for (int i = 0; i < n; i++){int fd = recvs[i].data.fd;int events = recvs[i].events;if (events & EPOLLERR){events |= EPOLLIN;}if (events & EPOLLHUP){events |= EPOLLIN;}if ((events & EPOLLIN) && IsConnectionSafe(fd)){if (_connections[fd]->_recv_cb){_connections[fd]->_recv_cb(_connections[fd]);}}if ((events & EPOLLOUT) && IsConnectionSafe(fd)){if (_connections[fd]->_send_cb){_connections[fd]->_send_cb(_connections[fd]);}}}}else if (n == 0){lg(Info, "Time Out...");}else{if (errno == EWOULDBLOCK)continue;lg(Warning, "Epoll error...");std::cout << "errno:" << errno << " strerror:" << strerror(errno) << std::endl;exit(1);}}}~ReactorServer() {}private:std::unique_ptr<Socket> _listensock;std::unique_ptr<Epoller> _epoller;std::unordered_map<int, std::shared_ptr<Connection>> _connections;uint16_t _port;func_t _handle_message;
};
#include "ReactorServer.hpp"void default_HandleMessage(std::shared_ptr<Connection> connection)
{
//根据服务器的服务内容编写此函数
//例如我这里想做一个计算器服务Calculator cal;std::string &inbuffer = connection->GetInBuffer();while (!inbuffer.empty()){std::string mes;int type;if (!CheckType(inbuffer, &type)){// 报文内容出现问题inbuffer = "";break;}if (type == 1){IntHandle(inbuffer, &mes);}else if (type == 2){DoubleHandle(inbuffer, &mes);}else{lg(Warning, "Type Error, type: %d ...", type);}connection->GetOutBuffer() += mes;connection->_send_cb(connection);}
}int main(int argc, char *argv[])
{if (argc != 2){std::cout << "Usage: ./selectServer port[8000-9000]" << std::endl;}ReactorServer ser(atoi(argv[1]), func_t(default_HandleMessage));ser.Init();ser.Start();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/44242.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

maven如何处理依赖冲突的问题?

Maven是一个强大的Java项目管理工具&#xff0c;它使用一个名为pom.xml的文件来管理项目的构建过程和依赖关系。当项目中出现依赖冲突时&#xff0c;Maven使用一系列策略来解决这些问题。以下是Maven处理依赖冲突的一些常见方法&#xff1a; 1. 最近优先策略&#xff1a; -…

tensorflow之欠拟合与过拟合,正则化缓解

过拟合泛化性弱 欠拟合解决方法&#xff1a; 增加输入特征项 增加网络参数 减少正则化参数 过拟合的解决方法&#xff1a; 数据清洗 增大训练集 采用正则化 增大正则化参数 正则化缓解过拟合 正则化在损失函数中引入模型复杂度指标&#xff0c;利用给w增加权重&#xff0c;…

点线面推进未来智造

如今&#xff0c;宁波拥有门类齐全的制造业体系&#xff0c;形成了以石油化工、汽车及零部件、电工电器、纺织服装等为支柱的产业集群。 宁波工业的发展并非一蹴而就&#xff0c;蓝卓总经理谭彰详细解读了宁波制造业的发展历程与当下目标&#xff0c;从工业小市到工业大市、工业…

基于Matlab和Python泰勒图的绘制

一、泰勒图介绍 泰勒图:泰勒图1常用于评价模型的精度,常用的精度指标有相关系数,标准差以及均方根误差(RMSE)。一般而言,泰勒图中的散点代表模型,辐射线代表相关系数,横纵轴代表标准差,而虚线代表均方根误差。泰勒图一改以往用散点图这种只能呈现两个指标来表示模型精度…

Qt使用笔记1(智能指针,deleteLater,多屏)

1、Qt智能指针 1.1、QPointer &#xff1a;解决野指针问题&#xff0c;必须是QObject对象。 1.2、QScopedPoint&#xff1a;作用域指针&#xff0c;出作用域自动释放。 1.3、QScopedArrayPoint&#xff1a;作用域数组指针&#xff0c;出作用域自动释放数组。 1.4、QSharedP…

RedHat运维-Ansible自动化运维基础20-从ansible-galaxy下载role

1. 社区管理的role仓库&#xff1a;___________________________________&#xff1b; 2. 社区管理的role仓库&#xff1a;___________________________________&#xff1b; 3. 社区管理的role仓库&#xff1a;___________________________________&#xff1b; 4. 在ansible …

Python数据结构的库之Fuk使用详解

概要 fuk 是一个用于处理 Python 数据结构的库,全称为 "Fast and Uncomplicated Kit"。它提供了一系列高效、简洁的数据结构实现,以及对 Python 内置数据结构的扩展。通过使用 fuk,开发者可以更加方便地处理列表、集合、字典等数据类型,提高代码的执行效率和可读…

vite+vue3拍照上传到nodejs服务器

一:效果展示: 拍照效果 二:Nodejs后端接口代码: 三:前端完整代码:

Vue基础--v-model/v-for/事件属性/侦听器

目录 一 v-model表单元素 1.1 v-model绑定文本域的value 1.1.1 lazy属性&#xff1a;光标离开再发请求 1.1.2 number属性&#xff1a;如果能转成number就会转成numer类型 1.1.3 trim属性&#xff1a;去文本域输入的前后空格 1.2v-model绑定单选checkbox 1.3代码展示 二 …

esp8266+micropython+irsend红外发射调试记录

在网上搜索esp8266micropython的红外发射库&#xff0c;没找到&#xff0c;发现 接收库是有的&#xff0c;可以参考&#xff1a;基于MicroPython的ESP8266连接外设IO&#xff08;二&#xff09;_micropython 红外接收-CSDN博客 可惜没有发射&#xff0c;很不方便。 这里都有介…

PHP财务记账管理系统小程序源码

理财小能手必备&#xff01;揭秘财务记账管理系统的魔力✨ &#x1f31f; 引入篇&#xff1a;告别糊涂账&#xff0c;拥抱财务自由 你是否曾为月底的账单头疼不已&#xff1f;是否觉得自己的钱总是莫名其妙地消失&#xff1f;别担心&#xff0c;财务记账管理系统来拯救你的钱…

【机器学习】必会数学知识:一文掌握数据科学核心数学知识点(下),收藏~

核心数学知识点 1、引言2、数据科学必会数学知识2.13 K均值聚类2.14 决策树2.15 随机森林2.16 梯度下降2.17 随机梯度下降&#xff08;SGD&#xff09;2.18 卷积2.19 拉普拉斯变换2.20 傅里叶变换2.21 信息论2.22 时间序列分析2.23 生成模型与判别模型2.24 支持向量机&#xff…

git merge 分支回退

1. 使用git merge --abort&#xff08;如果合并正在进行中&#xff09; git merge --abort2. 使用git reset&#xff08;合并已经提交&#xff09; 硬重置&#xff08;--hard&#xff09;会丢弃所有合并后的更改&#xff0c;并将HEAD指向合并前的提交。这是最彻底的方式&…

DDOS 攻击原理

DDoS攻击的基本原理 资源耗尽&#xff1a;攻击者通过发送大量的请求或数据包&#xff0c;耗尽目标系统的资源&#xff08;如带宽、CPU、内存等&#xff09;&#xff0c;使其无法处理正常的用户请求。 分布式攻击&#xff1a;与传统的DoS&#xff08;拒绝服务&#xff09;攻击不…

Java-面向对象基础

在面向对象编程&#xff08;Object-Oriented Programming, OOP&#xff09;中&#xff0c;类&#xff08;Class&#xff09;是一种用来描述对象共同特征的蓝图或模板。它是创建对象的原型&#xff0c;定义了对象可以有的属性&#xff08;字段&#xff09;和行为&#xff08;方法…

物联网应用,了解一点 WWAN全球网络标准

WWAN/蜂窝无线电认证&#xff0c;对跨地区应用场景&#xff0c;特别重要。跟随全球业务的脚步&#xff0c;我们像大唐先辈一样走遍全球业务的时候&#xff0c;了解一点全球化的 知识信息&#xff0c;就显得有那么点意义。 NA &#xff08;北美&#xff09;&#xff1a;美国和加…

OpenSSH漏洞(CVE-2024-6387)

漏洞信息 请参考:OpenSSH远程代码执行漏洞 (CVE-2024-6387) 影响范围 8.5p1 <= OpenSSH < 9.8p1 参考链接中内容补充 利用方式 关于OpenSSH漏洞CVE-2024-6387,攻击者可以利用一个有用的代码路径并在正确的时间点上被SIGALRM信号打断,从而导致sshd(OpenSSH守护…

基于Python的哔哩哔哩数据分析系统设计实现过程,技术使用flask、MySQL、echarts,前端使用Layui

背景和意义 随着互联网和数字媒体行业的快速发展&#xff0c;视频网站作为重要的内容传播平台之一&#xff0c;用户量和内容丰富度呈现爆发式增长。本研究旨在设计并实现一种基于Python的哔哩哔哩数据分析系统&#xff0c;采用Flask框架、MySQL数据库以及echarts数据可视化技术…

工控、物联网、电力行业调试工具大全

工控、物联网、电力行业调试工具大全 文章目录 工控、物联网、电力行业调试工具大全Modbus调试工具MQTT调试工具IEC104/IEC101调试工具IEC61850 调试工具DL/T645 调试工具 Modbus调试工具 Modbus 是一种用于工业自动化系统中的通信协议&#xff0c;最早由 Modicon&#xff08;…

信号111

2、核心转储 当进程出现魔种异常的时候&#xff0c;是否有OS将当前进程在内存中的相关核心数据&#xff0c;转存到磁盘中&#xff01; 一般而言云服务器的核心转储功能是关闭的。 3、验证进程等待中的core dump标记位。 4、为什么生产环境一般都要关闭 core dump 系统调用接口…