事件处理模式--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,一经查实,立即删除!

相关文章

java+jsp+Oracle+Tomcat 记账管理系统论文(一)

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️ ➡️点击免费下载全套资料:源码、数据库、部署教程、论文、答辩ppt一条龙服务 ➡️有部署问题可私信联系 ⬆️⬆️⬆️​​​​​​​⬆️…

laravel rabbitmq 队列

安装Laravel的RabbitMQ队列驱动&#xff1a; composer require vladimir-yuldashev/laravel-queue-rabbitmq env文件配置 #rabbitmq QUEUE_CONNECTIONrabbitmq #修改一下 RABBITMQ_HOST192.168.11.4 #要连接的主机名 RABBITMQ_PORT5671 #端口号 RABBITMQ_VHOST/…

自动化测试用例之元素自愈:Playwright与pytest的结合使用

前言 在自动化测试领域&#xff0c;元素定位的准确性对于测试的成功至关重要。当使用Playwright结合pytest进行测试时&#xff0c;我们可以通过一些策略来增强测试的鲁棒性&#xff0c;特别是在元素定位失败时能够自动进行修复。本文将详细介绍如何实现这一过程。 环境准备 …

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

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

Java jstat 基本使用 gc 查看,jstat -gcutil等

jstat&#xff08;Java Statistics Monitoring Tool&#xff09;是JDK自带的一个命令行工具&#xff0c;用于监视Java虚拟机&#xff08;JVM&#xff09;的各种运行时性能统计信息&#xff0c;如垃圾收集、内存使用情况等。它允许用户无需附加到目标Java进程中&#xff0c;即可…

YOLOv8核心原理深度解析

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

10种新兴网络安全威胁和攻击手法

网络攻击 第一 种新型勒索软件攻击 在当今互联网世界中&#xff0c;勒索软件已成为企业和个人面临的严峻威胁。根据Akamai发布的《互联网状态&#xff08;SOTI&#xff09;报告》&#xff0c;在不断发展的勒索软件环境中&#xff0c;攻击者正试图突破受害者的防御能力。与此同…

Vue3 + Element-plus 报错 require is not defined 处理问题

问题复现&#xff1a; yarn dev 后报错如下&#xff1a; app.js:358 Uncaught ReferenceError: require is not defined at eval (index.mjs:4:30) at Module../node_modules/element-plus/icons-vue/dist/es/index.mjs (chunk-vendors.js:9072:1) at webpack_require (app.j…

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;并减少由于缺陷产品导致的潜…

Vue3 + TS + Element-Plus 封装的 Table 表格组件

代码中主要增加了3个插槽&#xff0c;operationsStart 从操作栏开头增加按钮&#xff0c;operationsStart 从操作栏结尾增加按钮&#xff0c;还有一个插槽用来自定义列的内容&#xff0c;就是 TableModel里面的Key <template><el-tableborderstripe:data"data&q…

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上的数据不再存在。 不过不用…

利用R语言自带函数快速探索数据

《R Graphics Cookbook》 chapter2 Quickly Exploring Data 快速浏览数据 为了非常快速地浏览数据&#xff0c;有时使用 R 自带的绘图函数很有用 Creating a Scatter Plot 创建一个散点图 要制作散点图&#xff0c;请使用 plot() 并向其传递一个 x 值的向量&#xff0c;后跟一…

xcode 15.3 连接iphone 14PM真机提示 is not available because it is unpair

错误提示&#xff1a;iPhone 14PM is not available because it is unpaired&#xff0c;Pair with the device in the XcodeDevices Window, and respond to anypairing prompts on the device. 1、退出XCode,断开连接设备 2 、在终端执行 sudo pkill usbmuxd 3 、重新打开x…

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;给金融机构和消费者带来了巨大的损…

Docker常用命令 镜像库设置

Docker常用命令 & 镜像库设置 1. 镜像操作2. 容器操作3. 网络操作4. Docker Compose操作5. Docker volume操作6. Docker run介绍7. 镜像库设置 1. 镜像操作 列出本地所有的镜像 docker images从远程仓库拉取镜像到本地 docker pull <image_name>删除本地的指定镜像…