多路IO—POll函数,epoll服务器开发流程

引言

"在计算机网络编程中,多路IO技术是非常常见的一种技术。其中,Poll函数和Epoll函数是最为常用的两种多路IO技术。这两种技术可以帮助服务器端处理多个客户端的并发请求,提高了服务器的性能。本文将介绍Poll和Epoll函数的使用方法,并探讨了在服务器开发中使用这两种技术的流程和注意事项。"

 

poll函数介绍

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

(man poll 调用)

函数说明: 跟select类似, 委托内核监控可读, 可写, 异常事件

函数参数:

fds: 一个struct pollfd结构体数组的首地址

    struct pollfd {

      int fd;//要监控的文件描述符,如果fd为-1, 表示内核不再监控

     short events; //输入参数, 表示告诉内核要监控的事件, 读事件, 写事件, 异常事件 

     short revents;//输出参数, 表示内核告诉应用程序有哪些文件描述符有事件发生   

    };

events/revents:

 POLLIN:可读事件,让内核监控读事件就要写这个

 POLLOUT: 可写事件,缓冲区未满就可写

 POLLERR: 异常事件

nfds: 告诉内核监控的范围, 具体是: 数组下标的最大值+1

timeout:

=0: 不阻塞, 立刻返回

-1: 表示一直阻塞, 直到有事件发生

>0: 表示阻塞时长, 在时长范围内若有事件发生会立刻返回;

  如果超过了时长也会立刻返回

函数返回值:

>0: 发生变化的文件描述符的个数

=0: 没有文件描述符发生变化

-1: 表示异常

poll函数开发流程

1 创建socket ,得到监听文件描述符,lfd ----- socket();

2 设置端口复用----------setsockopt()

3 绑定 ------ bind()

4

struct pollfd client[1024]; client[0].fd = lfd;      // 放在哪都行,放在最俩头方便使用client[0].events = POLLIN;  //监控读事件,如果也让其监控可写事件,用或// 设置为fd 为-1 ,表示内核不在监控,这是一个初始化int maxi = 0;   //  定义最大数组下标for(int i = 0;i < 1024;i  ++){client[i].fd = -1;}//委托内核持续监控k= 0;while(1){nready = poll(client,maxi + 1,-1);//异常情况if(nready < 0 ){if(error == EINTR){continue;}break;}if(client[0].revents = POLLIN){//接受新的客户端连接k ++;cfd  = Accept(lfd,NULL,NULL);/*继续委托内核监听事件寻找在client 数组中可用位置*/for(i  = 0;i < 1024;i ++ ){if(client[i ].fd ==-1 ){client.fd[i] =  cfd;client.fd[i] = POLLIN;break;}}//客户端连接数达到最大值if(i == 1024){close(cfd);continue;   //退出,可能会有客户端连接退出,方便继续寻找}//修改client 数组下标最大值 if(maxi < i )maxi = i;if(--nready == 0 )continue;}//下面是有客户端发送数据的情况for(i = 1;i <=  maxi;i ++){//如果client数组中fd 为-1,表示已经不再让内核监控了if(client[i].fd == -1)continue;if(client[i].revents == POLLIN){sockfd =  client[i].fd;memset(buf,0x00,sizeof(buf));//read 数据n  =  Read(sockfd, buf,sizeof(buf));if(n <= 0){printf("read error or client closed,n =[%d]\n",n);close(sockfd);client[i].fd = -1;    //告诉内核不再监控}else {printf("read error,n == [%d],buf==[%s]\n,"n,buf);//发送数据给客户端Write(sockfd,buf,n);}if(--nready == 0 ){break;}}}close(lfd);}

多路IO-epoll     (重点)

将检测文件描述符的变化委托给内核去处理, 然后内核将发生变化的文件描述符对应的
事件返回给应用程序.

头文件

#include <sys/epoll.h>

函数

int epoll_create(int size) 

函数说明:创建一棵poll树,返回一个数根节点

函数参数:size:必须传一个大于0的数

返回值:返回个文件描述符,这个文件描述符就表示epoll树的树根节点

int  epoll_ctl(int epfd,int op,int fd,struct epoll_event *event)

函数说明:将fd上的epoll树,从树上删除和修改

函数参数: 

               epfd:epoll树的树根节点

op:

               EPOLL_CTL_ADD: 添加事件节点到树 上
               EPOLL_CTL_DEL: 从树上删除事件节点
               EPOLL_CTL_MOD: 修改树上对应的事件节点

fd:要操做的文件描述符

event :
        event.events 常用的有:
              EPOLLIN: 读事件
              EPOLLOUT: 写事件   
              EPOLLERR: 错误事件
              EPOLLET: 边缘触发模式


event.fd: 要监控的事件对应的文件描述符

typedef union epoll_data{

         void  *ptr;

          int     fd;

          uint32_t  u32;

          uint64_t  u64;

 }epoll_data_t;

struct epoll_event{

       uint32  events;    / *  Epoll events */

        epoll_data data;      /* User data variable */

};

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

函数说明:等待内核返回事件发生

参数说明:

      epfd: epoll树根

      events: 传出参数, 其实是一个事件结构体数组

      maxevents: 数组大小

timeout:

      -1: 表示永久阻塞

      0: 立即返回

      >0: 表示超时等待事件

返回值:

成功: 返回发生事件的个数

失败: 若timeout=0, 没有事件发生则返回; 返回-1, 设置errno值,

使用epoll 模型开发服务器流程

       1:创建socket,得到监听文件描述符lfd ---- socket()

       2:  设置端口复用 -----  setsockopt()

       3:  绑定 ------ bind()

       4:  监听 -------- listen() 

       5.  创建一棵epoll树      

开发完整的代码

//EPOLL 模型测试
#include "wrap.h"
#include <sys/epoll.h> 
#include <ctype.h>
int main()
{int ret;int n;int nready;int lfd;int cfd;int sockfd;char buf[1024];socklen_t  socklen;struct sockaddr_in svraddr;struct epoll_event ev;struct epoll_event events[1024];int k;int i;//创建socketlfd = Socket(AF_INET,SOCK_STREAM,0);//设置文件描述符为端口复用int opt = 1;setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));//绑定svraddr.sin_family = AF_INET;svraddr.sin_addr.s_addr = htonl(INADDR_ANY);svraddr.sin_port = htons(8888);Bind(lfd,(struct sockaddr *)&svraddr,sizeof(struct sockaddr_in));//ListenListen(lfd,128);//创建一棵epoll树int epfd = epoll_create(1024);if(epfd < 0 ){perror("create epoll error");return -1;} ev.data.fd = lfd;ev.events = EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);   //lfd 对应的事件节点上树while(1){nready = epoll_wait(epfd,events,1024,-1);  //等待内核返回事件 if(nready < 0){perror("epoll_wait error");if(nready == EINTR)   //判断是否收到了中断信号 {continue;}break;}for(i = 0;i < nready;i ++)   //小于发生事件的个数 {//有客户端连接发来请求 sockfd = events[i].data.fd;if(sockfd == lfd)        {cfd = Accept(lfd,NULL,NULL);ev.data.fd = cfd;ev.events = EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);}//有客户端发送数据过来else {memset(buf,0x00,sizeof(buf));n = Read(sockfd,buf,sizeof(buf));if(n <= 0){close(sockfd);epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL); //把sockfd从epfd树上删除 } else {for(k = 0;k < n;k ++){buf[k] = toupper(buf[k]);  //返回大写 }Write(sockfd,buf,n);}}}}close(epfd);close(lfd);return 0;
}   

epoll 的两种模式 ET 和 LT 模式
 

epoll 的LT模式:

     epoll 默认情况是LT模式,在这种情况下,如果读数据一次性没有读完,

     缓冲区还有可读数据,则epoll_wait还会再次通知。

epoll 的ET模式:

    如果将epoll设置为ET模式,若读数据的时候一次性没有读完,则epoll_wait不再通知

    直到下次有新的数据

用ET模式下,为了防止第二个客户端可以正常连接,并且发送数据,需要将socket设置为非阻塞模式

ET设置了非阻塞模式是因为使用了边缘触发模式(EPOLLET)。在边缘触发模式下,当有数据可读时,只会触发一次EPOLLIN事件,如果该次读取没有将缓冲区中的数据全部读取完毕,下次还是会触发EPOLLIN事件。因此,为了保证每次读取完整的数据,需要将socket设置为非阻塞模式,避免在缓冲区没有全部读取完毕时进行阻塞。

代码:

//EPOLL 模型测试 ET
#include "wrap.h"
#include <sys/epoll.h> 
#include <ctype.h>
#include <fcntl.h> 
int main()
{int ret;int n;int nready;int lfd;int cfd;int sockfd;char buf[1024];socklen_t  socklen;struct sockaddr_in svraddr;struct epoll_event ev;struct epoll_event events[1024];int k;int i;//创建socketlfd = Socket(AF_INET,SOCK_STREAM,0);//设置文件描述符为端口复用int opt = 1;setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));//绑定svraddr.sin_family = AF_INET;svraddr.sin_addr.s_addr = htonl(INADDR_ANY);svraddr.sin_port = htons(8888);Bind(lfd,(struct sockaddr *)&svraddr,sizeof(struct sockaddr_in));//ListenListen(lfd,128);//创建一棵epoll树int epfd = epoll_create(1024);if(epfd < 0 ){perror("create epoll error");return -1;} ev.data.fd = lfd;ev.events = EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);   //lfd 对应的事件节点上树while(1){nready = epoll_wait(epfd,events,1024,-1);  //等待内核返回事件 if(nready < 0){perror("epoll_wait error");if(nready == EINTR)   //判断是否收到了中断信号 {continue;}break;}for(i = 0;i < nready;i ++)   //小于发生事件的个数 {//有客户端连接发来请求 sockfd = events[i].data.fd;if(sockfd == lfd)        {cfd = Accept(lfd,NULL,NULL);ev.data.fd = cfd;ev.events = EPOLLIN | EPOLLET;  //epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);//将cfd设置为非阻塞模式int flag = fcntl(cfd, F_GETFL);flag |= O_NONBLOCK;   //O_NONBLOCK(非阻塞)标志位置为1。fcntl(cfd, F_SETFL, flag);}//有客户端发送数据过来else {memset(buf,0x00,sizeof(buf));while(1){n = Read(sockfd,buf,sizeof(buf));printf("n == [%d]\n",n);if(n == -1){printf("read over,n == [%d]\n",n);break;}if(n < 0 || (n <0 && n!=-1))    //对方关闭连接,或者异常的情况 {printf("n == [%d],buf == [%s]\n",n,buf);close(sockfd);epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL); //把sockfd从epfd树上删除 break;} else {printf("n == [%d],buf == [%s]\n",n,buf);for(k = 0;k < n;k ++){buf[k] = toupper(buf[k]);  //返回大写 }Write(sockfd,buf,n);}}}}}close(epfd);close(lfd);return 0;
}   

图解epoll反应堆流程

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

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

相关文章

TiDB x 北京银行丨新一代分布式数据库的探索与实践

导读 随着业务规模的扩大&#xff0c;传统数据库面临诸多限制&#xff0c;分布式数据库成为解决之道。本文 介绍了北京银行在数字化转型过程中对分布式数据库技术的探索&#xff0c;分享了 TiDB 在北京银行的应用历程和未来展望 。 本文根据北京银行软件开发中心罗水华先生在…

VS2019 C# mysql数据库使用EF

mysql 安装mysql-8.0.18-winx64 mysql-connector-net-8.0.18.msi mysql数据库.net开发驱动&#xff0c; 要在工程中引入connector安装后目录中的mysql.data.dll;如果直接在nutget中下载mysql.data.dll&#xff0c;那么就不用下载.net开发驱动包 mysql-for-visualstudio-1.…

设计模式_状态模式

状态模式 介绍 设计模式定义案例问题堆积在哪里解决办法状态模式一个对象 状态可以发生改变 不同的状态又有不同的行为逻辑游戏角色 加载不同的技能 每个技能有不同的&#xff1a;攻击逻辑 攻击范围 动作等等1 状态很多 2 每个状态有自己的属性和逻辑每种状态单独写一个类 角色…

Spring底层原理(四)

Spring底层原理(四) 本章内容 模拟实现Spring中的几个常见BeanFactory后置处理器 常见的BeanFactory后置处理器 GenericApplicationContext context new GenericApplicationContext(); context.registerBean("config",Config.class); context.registerBean(Conf…

YOLOv7优化:独家创新(Partial_C_Detect)检测头结构创新,实现涨点 | 检测头新颖创新系列

💡💡💡本文独家改进:独家创新(Partial_C_Detect)检测头结构创新,适合科研创新度十足,强烈推荐 SC_C_Detect | 亲测在多个数据集能够实现大幅涨点 收录: YOLOv7高阶自研专栏介绍: http://t.csdnimg.cn/tYI0c ✨✨✨前沿最新计算机顶会复现 🚀🚀🚀YOLO…

试题二(15分)和试题三(15分) (软件设计师笔记)

&#x1f3e0;个人主页&#xff1a;尘觉主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是尘觉&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&#x1f609; 在csdn获奖荣誉: &#x1f3c6;csdn城市之星2名 ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣…

模糊C均值聚类(FCM)python

目录 一、模糊C均值聚类的原理 二、不使用skfuzzy的python代码 三、 使用skfuzzy的python代码 一、模糊C均值聚类的原理 二、不使用skfuzzy的python代码 import numpy as np import random import matplotlib.pyplot as plt plt.rcParams[font.sans-serif][SimHei] plt.r…

保障效率与可用,分析Kafka的消费者组与Rebalance机制

系列文章目录 上手第一关&#xff0c;手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么&#xff0c;以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 Kafka存取原理与实现分析&#xff0c;打破面试难关 防止消息丢失与消息重复——Kafka可…

【WinForm详细教程四】WinForm中的ProgressBar 、ImageList和ListView控件

文章目录 1.ProgressBar2. ImageList3.ListView控件 1.ProgressBar 用于显示某个操作的进度。 属性&#xff1a; Value: 表示当前进度条的值&#xff0c;其范围由Min和Max决定。Step: 设置每次调用PerformStep()方法时增加的步长。MarqueeAnimationSpeed: 在Style设置为Marq…

二叉树问题——前/中/后/层遍历问题(递归与栈)

摘要 博文主要介绍二叉树的前/中/后/层遍历(递归与栈)方法 一、前/中/后/层遍历问题 144. 二叉树的前序遍历 145. 二叉树的后序遍历 94. 二叉树的中序遍历 102. 二叉树的层序遍历 103. 二叉树的锯齿形层序遍历 二、二叉树遍历递归解析 // 前序遍历递归LC144_二叉树的前…

MySQL连接的原理⭐️4种优化连接的手段性能提升240%

MySQL连接的原理⭐️4种优化连接的手段性能提升240%&#x1f680; 前言 上两篇文章我们说到MySQL优化回表的三种方式&#xff1a;索引条件下推ICP、多范围读取MRR与覆盖索引 MySQL的优化利器⭐️索引条件下推&#xff0c;千万数据下性能提升273%&#x1f680; MySQL的优化…

黄金矿工小游戏

欢迎来到程序小院 黄金矿工 玩法&#xff1a;点击开始游戏&#xff0c;黄金和钩子&#xff0c;钩子会左右摆动&#xff0c;对准黄金位置点击鼠标左键钓起黄金加对应时间&#xff0c;钓起黑色四块减去响应时间&#xff0c;快去挖矿吧^^。开始游戏https://www.ormcc.com/play/ga…

主播直播美颜SDK:提升颜值的秘诀

当下&#xff0c;主播们往往依赖于主播直播美颜SDK&#xff0c;这个技术工具为他们提供了一个让自己看起来更好看的机会。本文将深入探讨主播直播美颜SDK的工作原理、应用和影响&#xff0c;揭示提升颜值的秘诀。 一、主播直播美颜SDK是什么&#xff1f; 主播直播美颜SDK是一…

Latex排版SIGGRAPH总结(持续总结中...)

本文学习总结自&#xff1a;How to use the ACM SIGGRAPH / TOG LaTeX template 相关文件&#xff1a;百度网盘 首先解压 “my paper” 中的文件&#xff0c;并用Latex打开mypaper.tex. 多行连等公式 \begin{equation}表示编号公式&#xff0c;\[ \]表示无编号公式 无编号\b…

JMeter:断言之响应断言

一、断言的定义 断言用于验证取样器请求或对应的响应数据是否返回了期望的结果。可以是看成验证测试是否预期的方法。 对于接口测试来说&#xff0c;就是测试Request/Response&#xff0c;断言即可以针对Request进行&#xff0c;也可以针对Response进行。但大部分是对Respons…

精益制造的工具与方法有什么区别?ECRS工时分析软件的功能和价值

精益制造是一套价值创造系统&#xff0c;它强调在生产过程中减少浪费、提高效率和质量&#xff0c;从而实现持续改进和优化。在精益制造的理念下&#xff0c;企业需要运用一系列的工具和方法来提升生产管理水平。这些工具和方法不仅包括传统的精益工具&#xff0c;如5S、持续改…

三.RocketMQ单机安装及集群搭建

RocketMQ单机安装及集群搭建 一&#xff1a;安装环境1.软硬件要求2.下载RocketMQ 二.安装单机MQ1.上传并解压2.目录介绍3.修改MQ启动时初始JVM内存4.启动NameServer与Broker5.测试RocketMQ 三.RocketMQ集群搭建1.集群概念特点2.集群模式分类3.集群工作流程4.双主双从集群搭建4.…

X64(64位)汇编指令与机器码转换原理

X64&#xff08;64位&#xff09;汇编指令与机器码转换原理 1 64位寻址形式下的ModR/M字节1.1 寻址方式1.2 寄存器编号 2 汇编指令转机器码2.1 mov rcx, 1122334455667788h2.2 mov rcx,[r8]与mov [r8],rcx2.3 mov rcx,[r8r9*2] 本文属于《 X86指令基础系列教程》之一&#xff…

Uniapp开发的开源盲盒系统源码

最近比较火的盲盒系统&#xff0c;该项目是基于uniapp开发的盲盒项目&#xff0c;有需要的朋友可以联系我&#xff0c;运营级的项目&#xff0c;本次开源的是uniapp前端模板&#xff0c;选用技术为JAVA&#xff0c;采用框架&#xff1a;spring bootmybatisvue开发。 通过node安…

Javassist讲解1(介绍,读写字节码)

Javassist讲解1&#xff08;介绍&#xff0c;读写字节码&#xff09; 介绍一、读写字节码1.如何创建新的类2.类冻结 介绍 javassist 使Java字节码操作变得简单&#xff0c;它是一个用于在Java中编辑字节码的类库&#xff1b; 它使Java程序能够在运行时定义一个新类&#xff0c;…