事件处理模式--reactor原理与实现

文章目录

      • reactor
      • api
      • code

reactor

reactor是是服务器的重要模型, 是一种事件驱动的反应堆模式
通过epoll_create() 创建句柄, epoll_ctrl()提前注册好不同的事件处理函数 , 当事件到来就由 epoll_wait () 获取同时到来的多个事件,并且根据数据的不同类型将事件分发给事件处理机制 (事件处理器),通过回调函数方式实现响应的功能(如创建客户端fd, 读/写IO)

优点:

  1. 响应快,不必为单个同步时间所阻塞,虽然 Reactor 本身依然是同步的;
  2. 编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/ 进程的切换开销
  3. 可扩展性,可以方便的通过增加 Reactor 实例个数来充分利用 CPU 资源
  4. 可复用性,reactor 框架本身与具体事件处理逻辑无关,具有很高的复用性

流程:

  1. 注册事件 和 对应的事件处理器
  2. 多路复用器等待事件到来
  3. 事件到来,激发事件分发器分发事件到对应的处理器
  4. 事件处理器处理事件,然后注册新的事件 (如fu武器接收buffer 后 发送buffer)

api

  • epol_create: 创建fd
int epoll_create(int size);

创建一个epoll的句柄,size通常为1,当创建好epoll句柄后,它就是会占用一个fd

  • epoll_ctrl: epoll的事件注册函数
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll_ctl向 epoll对象中添加、修改或者删除感兴趣的事件,返回0表示成功,否则返回–1,此时需要根据errno错误码判断错误类型
epfd : epoll_create()的返回值
op : 表示动作
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;

fd : 需要监听的fd

*event : 告诉内核需要监听什么事

struct epoll_event结构:

typedef union epoll_data {void *ptr;int fd;__uint32_t u32;__uint64_t u64;
} epoll_data_t;struct epoll_event {__uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */
};
  • epoll_wait: 等待事件产生,类似与select调用
 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

epfd: epoll的描述符
*event : events则是分配好的 epoll_event结构体数组,epoll将会把发生的事件复制到 events数组中
maxevnets : 表示本次可以返回的最大事件数目,通常 maxevents参数与预分配的events数组的大小是相等的。
timeout : 表示在没有检测到事件发生时最多等待的时间(单位为毫秒),如果 timeout为0,则表示 epoll_wait在 rdllist链表中为空,立刻返回,不会等待

code

reactor 封装

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <fcntl.h>typedef struct sockaddr SA;#define BUFFSIZE 1024struct sockitem {int sockfd;//事件处理器,处理函数回调接口int (*callback)(int fd, int events, void* arg);//读写函数char recvbuffer[BUFFSIZE];char sendbuffer[BUFFSIZE];//读写字节数int rlen;int slen;
};struct reactor {int epfd;struct epoll_event events[512];
};//定义全局的eventloop --> 事件循环
struct reactor* eventloop = NULL;//申明这些事件处理器函数
int recv_cb(int fd, int events, void *arg);
int accept_cb(int fd, int events, void* arg);
int send_cb(int fd, int evnts, void* arg);int recv_cb(int fd, int events, void *arg) {struct sockitem* si = (struct sockitem*)arg;struct epoll_event ev;//后面需要 //处理IO读事件int ret = recv(fd, si->recvbuffer, BUFFSIZE, 0);if (ret < 0) {if (errno == EAGAIN || errno == EWOULDBLOCK) { //return -1;} else {}//出错了,从监视IO事件红黑树中移除结点,避免僵尸结点ev.events = EPOLLIN;epoll_ctl(eventloop->epfd, EPOLL_CTL_DEL, fd, &ev);close(fd);free(si);		} else if (ret == 0) {//对端断开连接printf("fd %d disconnect\n", fd);ev.events = EPOLLIN;epoll_ctl(eventloop->epfd, EPOLL_CTL_DEL, fd, &ev);//close同一断开连接,避免客户端大量的close_wait状态close(fd);free(si);	} else {//打印接收到的数据printf("recv: %s, %d Bytes\n", si->recvbuffer, ret);//设置sendbuffersi->rlen = ret;memcpy(si->sendbuffer, si->recvbuffer, si->rlen);si->slen = si->rlen;//注册写事件处理器struct epoll_event ev;ev.events = EPOLLOUT | EPOLLET;si->sockfd = fd;si->callback = send_cb;ev.data.ptr = si;epoll_ctl(eventloop->epfd, EPOLL_CTL_MOD, fd, &ev);}}
int accept_cb(int fd, int events, void* arg) {//处理新的连接。 连接IO事件处理流程struct sockaddr_in cli_addr;memset(&cli_addr, 0, sizeof(cli_addr));socklen_t cli_len = sizeof(cli_addr);int cli_fd = accept(fd, (SA*)&cli_addr, &cli_len);if (cli_fd <= 0) return -1;char cli_ip[INET_ADDRSTRLEN] = {0};	//存储cli_ipprintf("Recv from ip %s at port %d\n", inet_ntop(AF_INET, &cli_addr.sin_addr, cli_ip, sizeof(cli_ip)),ntohs(cli_addr.sin_port));//注册接下来的读事件处理器struct epoll_event ev;ev.events = EPOLLIN | EPOLLET;struct sockitem* si = (struct sockitem*)malloc(sizeof(struct sockitem));si->sockfd = cli_fd;si->callback = recv_cb;//设置事件处理器ev.data.ptr = si;epoll_ctl(eventloop->epfd, EPOLL_CTL_ADD, cli_fd, &ev);return cli_fd;}
int send_cb(int fd, int events, void* arg) {//处理send IO事件struct sockitem *si = (struct sockitem*)arg;send(fd, si->sendbuffer, si->slen, 0); //再次注册IO读事件处理器struct epoll_event ev;ev.events = EPOLLIN | EPOLLET;si->sockfd = fd;si->callback = recv_cb;//设置事件处理器ev.data.ptr = si;epoll_ctl(eventloop->epfd, EPOLL_CTL_MOD, fd, &ev);}int main(int argc, char* argv[]) {if (argc != 2) {fprintf(stderr, "uasge: %s <port>", argv[0]);return 1;}int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in serv_addr;int port = atoi(argv[1]);//确定服务端协议地址簇memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = INADDR_ANY;serv_addr.sin_port = htons(port);//进行绑定if (-1 == bind(sockfd, (SA*)&serv_addr, sizeof(serv_addr))) {fprintf(stderr, "bind error");return 2;}if (-1 == listen(sockfd, 5)) {fprintf(stderr, "listen error");return 3;}//init eventloopeventloop = (struct reactor*)malloc(sizeof(struct reactor));//创建epoll句柄.eventloop->epfd = epoll_create(1);//注册建立连接IO事件处理函数struct epoll_event ev;ev.events = EPOLLIN;struct sockitem* si = (struct sockitem*)malloc(sizeof(struct sockitem));si->sockfd = sockfd;si->callback = accept_cb;//设置事件处理器ev.data.ptr = si;//将监视事件加入到reactor的epfd中epoll_ctl(eventloop->epfd, EPOLL_CTL_ADD, sockfd, &ev);while (1) {//多路复用器监视多个IO事件int nready = epoll_wait(eventloop->epfd, eventloop->events, 512, -1);if (nready < -1) {break;}int i = 0;//循环分发所有的IO事件给处理器for (i = 0; i < nready; ++i) {if (eventloop->events[i].events & EPOLLIN) {struct sockitem* si = (struct sockitem*)eventloop->events[i].data.ptr;si->callback(si->sockfd, eventloop->events[i].events, si);} if (eventloop->events[i].events & EPOLLOUT) {struct sockitem* si = (struct sockitem*)eventloop->events[i].data.ptr;si->callback(si->sockfd, eventloop->events[i].events, si);}}}return 0;
}

原文链接:https://blog.csdn.net/weixin_53695360/article/details/123894158

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

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

相关文章

ZooKeeper 搭建详细步骤之一(单机模式)

ZooKeeper 搭建详细步骤之三&#xff08;真集群&#xff09; ZooKeeper 搭建详细步骤之二&#xff08;伪集群模式&#xff09; ZooKeeper 搭建详细步骤之一&#xff08;单机模式&#xff09; ZooKeeper 及相关概念简介 搭建模式简述 ZooKeeper 的搭建模式包括单机模式、集群模…

YOLOv8核心原理深度解析

YOLOv8源码地址: https://github.com/ultralytics/ultralytics 一、简介: 根据官方描述,Yolov8是一个SOTA模型,它建立在Yolo系列历史版本的基础上,并引入了新的功能和改进点,以进一步提升性能和灵活性,使其成为实现目标检测、图像分割、姿态估计等任务的最佳选择。其具体…

AI图书推荐:将 ChatGPT和Excel融合倍增工作效率

《将 ChatGPT和Excel融合倍增工作效率》&#xff08; Hands-on ChatGPT in Excel. Enhance Your Excel Workbooks&#xff09;由Mitja Martini撰写&#xff0c;旨在教授读者如何将ChatGPT与Excel结合使用&#xff0c;以提升工作效率和创造AI增强的Excel工具。它还提供了Excel中…

AnomalyGPT——使用大型视觉语言模型进行工业异常检测的算法解析与应用

1.概述 工业缺陷检测是工业自动化和质量控制中的一个重要环节&#xff0c;其目的是在生产过程中识别和分类产品或组件中的缺陷&#xff0c;以确保最终产品的质量满足既定标准。这项技术的应用可以显著提高生产效率&#xff0c;降低成本&#xff0c;并减少由于缺陷产品导致的潜…

sgg_ssm学习--前端搭建遇到的问题

目录 问题1&#xff1a;由于我是解压缩软件nodejs&#xff0c;没有添加系统路径 解决&#xff1a;添加nodejs的路径 到系统 path中 问题2&#xff1a;vscode 终端输入npm命令 报错 解决(如图所示在vscode打开前端工程&#xff0c;终端修改如下配置)&#xff1a; 问题1&…

如何在iPhone上恢复出厂设置后恢复数据

你不想让这种情况发生&#xff0c;但它确实发生了。您必须将iPhone恢复出厂设置。当您的 iPhone 上出现软件问题且无法修复时&#xff0c;可能会发生这种情况。相反&#xff0c;在更新期间&#xff0c;或者您的iPhone遇到问题时&#xff0c;iPhone上的数据不再存在。 不过不用…

Spark SQL编程初级实践

参考链接 Spark编程: Spark SQL基本操作 2020.11.01_df.agg("age"->"avg")-CSDN博客 RDD编程初级实践-CSDN博客 Spark和Hadoop的安装-CSDN博客 1. Spark SQL基本操作 { "id":1 , "name":" Ella" , "age":…

使用多网络和Kubernetes转变您的通讯应用程序

传统的 Kubernetes 网络在基本的 Pod 到 Pod 连接方面表现出色&#xff0c;但在满足通讯工作负载的安全性、性能和合规性要求时可能存在不足。这限制了通讯提供商充分利用 Kubernetes 的可扩展性和敏捷性优势的能力。 Google Cloud 的多网络方法使通讯提供商能够克服这些限制&a…

笔记-PPT绘图导出高清无失真图片

问题描述&#xff1a;PPT绘图已经用了高清图&#xff08;jpg、tif格式&#xff09;&#xff0c;但论文图片还是不清晰&#xff0c;打印出来还是有点糊 以下是PPT导出高清不失真图片&#xff08;emf格式&#xff09;的具体描述。 目录 一、绘图工具二、操作步骤 一、绘图工具 …

数据挖掘之基于Lightgbm等多模型消融实验的信用欺诈检测实现

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 在当前的金融环境中&#xff0c;信用欺诈行为日益增多&#xff0c;给金融机构和消费者带来了巨大的损…

【已解决】pandas读excel中长数字变成科学计数法的问题

pandas 读excel中的长数字时&#xff0c;即使excel中已经设置为文本&#xff0c;读进df后也会自动变成科学计数法。 在日常的数据分析和处理工作中&#xff0c;Excel和pandas是数据分析师们不可或缺的得力助手。然而&#xff0c;在使用pandas读取Excel文件时&#xff0c;我们有…

Spring Boot的热部署工具“AND”Swagger测试工具

Spring Boot的热部署&Swagger测试页面的使用 热部署指的是在项目无需重启的情况下&#xff0c;只需要刷新页面&#xff0c;即可获得已经修改的样式或功能。要注意该工具一般用于开发环境&#xff0c;在生产环境中最好不要添加这个工具。 对于无需重启便可刷新这么方便的工…

【linux-1-Ubuntu常用命令-vim编辑器-Vscode链接ubuntu远程开发】

目录 1. 安装虚拟机Vmare和在虚拟机上安装Ubuntu系统&#xff1a;2. 常用的Ubuntu常识和常用命令2.1 文件系统结构2.2 常用命令2.3 vim编辑器 3. Ubuntu能联网但是ping不通电脑&#xff1a;4. Windows上安装VScode链接ubuntu系统&#xff0c;进行远程开发&#xff1a; 1. 安装虚…

理解sourcemap:调试和部署的利器

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

根据顶层的id递归查询出全部子节点

效果图 根据输入的id为2查询出所有的红色框起来的节点 mapper接口 TSystemOrg getOrgByorgId(String orgId); List<TSystemOrg> getOrgListByParentId(String parentId);mapper.xml <!--根据id查询org--> <select id"getOrgByorgId" resultType&…

【JVM】简述类加载器及双亲委派机制

双亲委派模型&#xff0c;是加载class文件的一种机制。在介绍双亲委派模型之前&#xff0c;我需要先介绍几种类加载器&#xff08;Class Loader&#xff09;。 1&#xff0c;类加载器 Bootstrap&#xff0c;加载lib/rt.jar&#xff0c;charset.jar等中的核心类&#xff0c;由…

【算法】【贪心算法】【leetcode】870. 优势洗牌

题目地址&#xff1a;https://leetcode.cn/problems/advantage-shuffle/description/ 题目描述&#xff1a; 给定两个长度相等的数组 nums1 和 nums2&#xff0c;nums1 相对于 nums2 的优势可以用满足 nums1[i] > nums2[i] 的索引 i 的数目来描述。 返回 nums1 的任意排列&…

界面组件DevExpress中文教程 - 如何在Node.js应用中创建报表?

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 获取DevExpress Reporting最新正式版下载(Q技术…

【SQL Server】入门教程-基础篇(三)

目录 前言 SQL 常用函数学习 AVG – 平均值 COUNT – 汇总函数 ​编辑MAX – 最大值 ​编辑MIN – 最小值 ​编辑SUM – 求和 UCASE/UPPER – 大写 LCASE/LOWER – 小写 ROUND – 数值取舍 NOW/SYSDATE – 当前时间 前言 这一篇博客&#xff0c;是Sql Server函数学…

本地构建编译Apache-Seatunnel2.3.5适配Web1.0.0运行实现Mysql-CDC示例

本地构建编译Apache-Seatunnel2.3.5适配Web1.0.0运行实现Mysql-CDC示例 文章目录 1.前言2.编译2.1版本说明2.2 seatunnel2.3.4-release分支配置2.3maven调优配置 3.web1.0.0适配3.1配置文件修改和新增文件3.2手动拷贝jar修改依赖3.3修改web不兼容的代码3.4 web编译打包 4.运行m…