高级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.rhkb.cn/news/374678.html

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

相关文章

【深度好文】合作伙伴关系管理自动化:双向共赢新趋势

在当今快速变化的商业环境中&#xff0c;合作伙伴关系已成为企业成功的关键因素之一。为了更高效地管理这些关系&#xff0c;合作伙伴关系管理自动化正逐渐成为行业的新趋势&#xff0c;它不仅简化了管理流程&#xff0c;更促进了双方共赢的局面。 一、传统管理 VS 自动化管理 …

【RHCE】实验(HTTP,DNS,SELinux,firewalld的运用)

一、题目 二、主服务器配置 1.下载HTTP服务&#xff0c;DNS服务 [rootlocalhost ~]# yum install -y httpd bind 2.开启防火墙&#xff0c;放行服务 # 开启防火墙 [rootlocalhost ~]# systemctl start firewalld # 放行服务 [rootlocalhost ~]# firewall-cmd --add-service…

【计算机毕业设计】012基于微信小程序的科创微应用平台

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

C语言-顺序表

&#x1f3af;引言 欢迎来到HanLop博客的C语言数据结构初阶系列。在这个系列中&#xff0c;我们将深入探讨各种基本的数据结构和算法&#xff0c;帮助您打下坚实的编程基础。本次我将为你讲解。顺序表&#xff08;也称为数组&#xff09;是一种线性表&#xff0c;因其简单易用…

Windows环境+C#实现显示接口测试

代码如下&#xff1a; using Models; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.Data; using System.Diagnostics; using System.Drawing; using System.IO; …

C++入门到进阶(图文详解,持续更新中)

C入门到进阶&#xff08;图文详解&#xff0c;持续更新中&#xff09; 目录 C入门到进阶&#xff08;图文详解&#xff0c;持续更新中&#xff09; 数据 数据类型 基本数据类型/内置数据类型 C常用运算符 赋值运算符 关系运算符 逻辑运算符 杂项运算符 数据的本地化…

NFS服务器、autofs自动挂载综合实验

综合实验 现有主机 node01 和 node02&#xff0c;完成如下需求&#xff1a; 1、在 node01 主机上提供 DNS 和 WEB 服务 2、dns 服务提供本实验所有主机名解析 3、web服务提供 www.rhce.com 虚拟主机 4、该虚拟主机的documentroot目录在 /nfs/rhce 目录 5、该目录由 node02 主机…

jmeter-beanshell学习7-props获取全局变量和设置全局变量

继续写点不痛不痒的小东西。第一篇写了vars设置变量&#xff0c;但是vars只能作用在同一个线程组。跨线程组情况比较少&#xff0c;要是用到跨线程组&#xff0c;有个pros&#xff0c;用法和vars一样。 在setup线程组设置变量a&#xff0c;执行的时候&#xff0c;jmeter会先执行…

Windows 电脑查看 WiFi 密码的方法都有哪些?

从设置面板中查看 当你使用的是笔记本电脑并且连接 WiFi 之后可以在设置面板中查看 WiFi 密码&#xff0c;首先打开设置界面&#xff0c;然后点击网络和 Internet&#xff0c;找到 WiFi 之后点击进入&#xff0c;然后点击管理已知网络。 然后点击已经连接好的无线网络。 进入之…

国产化趋势下源代码数据防泄密的信创沙盒的方案分享

随着国产化的大力推进&#xff0c;越来越多的企事业单位在逐步替换Windows、Linux等操作系统的使用。那么什是国产化了&#xff1f;国产化是指在产品或服务中采用国内自主研发的技术和标注&#xff0c;替代过去依赖的他国的产品和服务&#xff0c;国产化又被称之为“信创”&…

215.Mit6.S081-实验三-page tables

在本实验室中&#xff0c;您将探索页表并对其进行修改&#xff0c;以简化将数据从用户空间复制到内核空间的函数。 一、实验准备 开始编码之前&#xff0c;请阅读xv6手册的第3章和相关文件&#xff1a; kernel/memlayout.h&#xff0c;它捕获了内存的布局。kernel/vm.c&…

macOS系统下载navicat安装包

链接: https://pan.baidu.com/s/1SqTIXNL-B8ZMJxIBu1DfIw?pwdc1z8 提取码: c1z8 安装后效果

虚幻引擎 快速的色度抠图 Chroma Key 算法

快就完了 ColorTolerance_PxRange为容差&#xff0c;这里是0-255的输入&#xff0c;也就是px单位&#xff0c;直接用0-1可以更快 Key为目标颜色

Kafka第四篇——生产数据总体概括,源码解析分区策略,数据收集器,Sender发送线程,key值

目录 流程图以及总体概述 拦截器 分区器以及分区计算策略 为啥进行分区计算&#xff1f; producer生产者怎么知道有哪些分区&#xff1f; 分区计算 如何自定义实现分区器&#xff1f; 想说的在图里啦&#xff01;宝宝&#xff01;&#x1f4a1; ​编辑 如果key值忘记传递了呢&a…

python+selenium-UI自动框架之[优化]元素查找和BasePage页面

痛点&#xff1a;在页面查找元素的时候会遇到找不到或者其他无法处理某个字段的情况&#xff0c;又或者想要在输出的log或者report里面显示这个字段名称&#xff0c;这时候加上字段名称就很重要&#xff01; [3]pythonselenium - UI自动框架之封装查找元素https://mp.csdn.net…

电脑的D盘E盘F盘突然消失了 电脑只剩下C盘了其他盘怎么恢复

现如今随着时代的发展&#xff0c;无纸化办公成为主流&#xff0c;这主要归功于电脑&#xff0c;能够通过电脑完成的工作绝不使用纸质文件&#xff0c;这不仅提高了工作效率&#xff0c;也让一些繁杂的工作变的更加简单。不过电脑毕竟是电子产品&#xff0c;不可避免的会出现一…

通信协议_Modbus协议简介

概念介绍 Modbus协议&#xff1a;一种串行通信协议&#xff0c;是Modicon公司&#xff08;现在的施耐德电气Schneider Electric&#xff09;于1979年为使用可编程逻辑控制器&#xff08;PLC&#xff09;通信而发表。Modbus已经成为工业领域通信协议的业界标准&#xff08;De f…

Navicat导入sql文件

文章目录 Navicat导入SQL文件&#xff0c;使用默认导入&#xff0c;不做任何修改报错尝试一修改运行时的选择 尝试二修改my.ini的配置文件 Navicat导入SQL文件&#xff0c;使用默认导入&#xff0c;不做任何修改报错 尝试一 修改运行时的选择 取消勾选 ‘每个运行中运行多重查…

一键掌握天气动态 - 基于Vue和高德API的实时天气查询

前言 本文将学习如何使用Vue.js快速搭建天气预报界面,了解如何调用高德地图API获取所需的天气数据,并掌握如何将两者有机结合,实现一个功能丰富、体验出色的天气预报应用 无论您是前端新手还是有一定经验,相信这篇教程都能为您带来收获。让我们一起开始这段精彩的Vue.js 高德…

Mac的系统数据怎么删除 cleanmymac会乱删东西吗 cleanmymac有用吗

作为一款专业级的苹果电脑清理软件&#xff0c;CleanMyMac可以精准识别系统垃圾&#xff0c;有效防止Mac系统数据被误删。软件可以深入系统底层&#xff0c;清理无用的系统数据&#xff0c;优化苹果电脑设置&#xff0c;提升Mac系统性能。有关Mac的系统数据可以删吗&#xff0c…