多路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.mzph.cn/news/128660.shtml

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

相关文章

算法通关村第四关-黄金挑战基础计算器问题

大家好我是苏麟 , 今天带来栈的比较难的问题 . 计算器问题 基础计算器 LeetCode 224 描述 : 给你一个字符串表达式 s &#xff0c;请你实现一个基本计算器来计算并返回它的值。 s 由数字、、-、(、)、和 组成s 表示一个有效的表达式 不能用作一元运算(例如&#xff0c; …

高效处理异常值的算法:One-class SVM模型的自动化方案

一、引言 数据清洗和异常值处理在数据分析和机器学习任务中扮演着关键的角色。清洗数据可以提高数据质量&#xff0c;消除噪声和错误&#xff0c;从而确保后续分析和建模的准确性和可靠性。而异常值则可能对数据分析结果产生严重影响&#xff0c;导致误导性的结论和决策。因此&…

C++类和对象下(初始化列表,静态成员,explicit关键字,友元)

C类和对象下[初始化列表,静态成员,explicit关键字,友元] 一.初始化列表1.为什么会有初始化列表2.初始化列表的语法形式3.没有默认构造函数的自定义成员变量4.初始化列表是成员变量定义的地方5.初始化列表可以跟函数体内定义搭配使用6.初始化列表执行的顺序7.总结建议 二.静态成…

当风格遇上浴缸:浴室装饰的秘诀

浴室不再仅仅是个洗漱的地方&#xff0c;如今它们是室内设计的一部分&#xff0c;有时甚至是焦点。浴室的装饰风格可以塑造整个房间的氛围&#xff0c;而浴缸通常是浴室内最引人注目的元素之一。在这里&#xff0c;我们将简单探讨不同室内设计风格与浴缸如何融合&#xff0c;让…

PlantSimulation安装帮助文档端口被占用的解决办法

PlantSimulation安装帮助文档端口被占用的解决办法 从PlantSimulaiton&#xff08;TPS&#xff09;2201开始帮助文档开始使用在线&#xff0c;如果使用本地则需要安装本地文档服务器。但是在安装过程中你可能会遇到&#xff0c;5000断开被占用的情况。解决办法如下&#xff1a…

C# 发送邮件

1.安装 NuGet 包 2.代码如下 SendMailUtil using MimeKit; using Srm.CMER.Application.Contracts.CmerInfo; namespace Srm.Mail { public class SendMailUtil { public async static Task<string> SendEmail(SendEmialDto sendEmialDto,List<strin…

java APP自动化测试AppIum

一、前言 二、Appium环境搭建 2.1 JDK安装 2.2 Android SDK安装配置 2.3 模拟器安装及配置 2.4 Appium Desktop安装及使用 2.5 Appium配置连接模拟器 三、实战基本脚本编写 3.1 创建Maven项目并配置 3.2 简单Demo 四、写在最后 一、前言 随着移动互联网的发展&#xff0c;AP…

mac录屏快捷键指南,轻松录制屏幕内容!

“大家知道mac电脑有录屏快捷键吗&#xff0c;现在录屏不太方便&#xff0c;每次都花很多时间&#xff0c;要是有录屏快捷键&#xff0c;应该会快速很多&#xff0c;可是哪里都找不到&#xff0c;有人知道吗&#xff1f;帮帮我&#xff01;” 苹果的mac电脑以其精美的设计和卓…

Java使用pdfbox进行pdf和图片之间的转换

简介 pdfbox是Apache开源的一个项目,支持pdf文档操作功能。 官网地址: Apache PDFBox | A Java PDF Library 支持的功能如下图.引入依赖 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox-app</artifactId><version>…

AIGC|把Azure Open AI和Jira集成起来,实现智能化项目管理

目录 一、Jira与Azure OpenAI介绍 二、Jira和Azure OpenAI的REST API对接 三、利用Chats插件实现对话的过程 四、总结 一、Jira与Azure OpenAI介绍 Jira是一款由澳大利亚公司Atlassian开发的项目管理工具&#xff0c;主要用于追踪问题、管理需求、构建报告和管理故障等事项…

ruby语言怎么写个通用爬虫程序?

Ruby语言爬虫是指使用Ruby编写的网络爬虫程序&#xff0c;用于自动化地从互联网上获取数据。其中&#xff0c;CRawler是一个基于文本的小型地牢爬虫&#xff0c;它被设计为可扩展&#xff0c;所有游戏数据均通过JSON文件提供&#xff0c;程序仅处理游戏引擎。除此之外&#xff…

EasyExcel复杂表头数据导入

目录 表头示例导入代码数据导出 表头示例 导入代码 Overridepublic void importExcel(InputStream inputStream) {ItemExcelListener itemExcelListener new ItemExcelListener();EasyExcel.read(inputStream, ImportItem.class, itemExcelListener).headRowNumber(2).sheet()…

OSPF 高级特性3

一、OSPF安全特性 1、OSPF报文验证&#xff1a; 区域验证模式&#xff1a;在区域下配置一致的密码才能加入同一个区域。 [r3-ospf-1-area-0.0.0.0]authentication-mode md5 1 cipher 123456 接口验证模式&#xff1a;链路两端的接口必须配置一致的密码才能建立邻居关系 [r5-Gig…

回归预测 | Matlab实现SO-CNN-SVM蛇群算法优化卷积神经网络-支持向量机的多输入单输出回归预测

Matlab实现SO-CNN-SVM蛇群算法优化卷积神经网络-支持向量机的多输入单输出回归预测 目录 Matlab实现SO-CNN-SVM蛇群算法优化卷积神经网络-支持向量机的多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.SO-CNN-SVM蛇群算法优化卷积神经网络-支持向量…

oracel处理XML时,报ORA-31011、ORA-19202。

原字段为clob&#xff0c; 查询 SELECT XMLTYPE(字段) FROM TABLE_A报错如下&#xff1a; ORA-31011: XML 语法分析失败 ORA-19202: XML 处理 LPX-00217: invalid character 12 (U000C) Error at line 1559时出错 ORA-06512: 在 "SYS.XMLTYPE", line 272 ORA-0651…

大洋钻探系列之一引子

大洋科学钻探计划自1968年启动开始&#xff0c;迄今已有50余年&#xff0c;先后经历了&#xff14;个阶段。深海钻探计划(Deep Sea Drilling Program&#xff0c;DSDP&#xff0c;1968-1983年&#xff09;、国际大洋钻探计划&#xff08;Ocean Drilling Program&#xff0c;ODP…

目标检测 图像处理 计算机视觉 工业视觉

目标检测 图像处理 计算机视觉 工业视觉 工业表盘自动识别&#xff08;指针型和数值型&#xff09;智能水尺识别电梯中电动车识别&#xff0c;人数统计缺陷检测&#xff08;半导体&#xff0c;电子元器件等&#xff09;没带头盔检测基于dlib的人脸识别抽烟检测和睡岗检测/驾驶疲…

【使用Python编写游戏辅助工具】第三篇:鼠标连击器的实现

前言 这里是【使用Python编写游戏辅助工具】的第三篇&#xff1a;鼠标连击器的实现。本文主要介绍使用Python来实现鼠标连击功能。 鼠标连击是指在很短的时间内多次点击鼠标按钮&#xff0c;通常是鼠标左键。当触发鼠标连击时&#xff0c;鼠标按钮会迅速按下和释放多次&#xf…

双十一快递“当天达”?宏电助力物流分拣系统高效运行

​众所周知&#xff0c;每年双11都是快递业务的高峰期&#xff0c;是对各大物流企业运输能力的一次大考。为了持续提升快递配送的速度&#xff0c;自动化物流仓储建设的速度也在不断的加快&#xff0c;而在一个完整的自动化物流仓储系统中&#xff0c;输送分拣设备是物流自动化…

MySql优化经验分享

一条sql的具体执行过程 连接 我们怎么查看MySQL当前有多少个连接&#xff1f; 可以用show status命令&#xff0c;模糊匹配Thread&#xff0c; Show global status like "Thread%" show global variables like wait timeout;—非交互式超时时间&#xff0c;如JDBC…