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

news2025/1/18 8:54:15

引言

"在计算机网络编程中,多路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;
	
	//创建socket
	lfd = 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));
	
	//Listen
	Listen(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;
	
	//创建socket
	lfd = 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));
	
	//Listen
	Listen(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.coloradmin.cn/o/1154048.html

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

相关文章

asp.net旅游交流管理信息系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net 旅游交流管理信息系统是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c# 语言开发 asp.net旅游交流网站1 应用技…

【脑机接口 论文】利用脑机接口帮助ALS患者恢复对家用设备的控制science

英文题目 中文题目 稳定的语音BCI解码使ALS患者在3个月内无需重新校准即可进行控制论文下载&#xff1a;算法程序下载&#xff1a;摘要1 项目介绍2 方法2.1实时神经解码2.2算法手术植入:神经解码模型: 数据收集实验2.3稳定的解码器性能超过三个月 3 电极的贡献4 讨论5结论 中文…

Linux shell编程学习笔记18:while循环语句

上回我们研究和探讨了Linux shell编程中for 循环语句&#xff0c;与在C/C中一样&#xff0c;for 循环语句Linux shell编程中有很多灵活的用法。今天我们来研究和探讨while循环语句。 一、数字条件循环 我们继续以for循环语句中的例子&#xff0c;计算 从1到10与2的乘积 并输出…

实时定位和配送追踪:开发万岳同城外卖APP的关键技术特性

随着生活节奏的不断加快&#xff0c;外卖服务已经成为许多人日常生活中不可或缺的一部分。无论是工作日的午餐&#xff0c;还是周末的家庭聚会&#xff0c;外卖APP已经成为满足各种美食需求的首选方式。然而&#xff0c;同城外卖APP的成功不仅仅取决于美味的食物选择&#xff0…

python 打印与去除不可见字符 \x00

# 此处不是真实的\x00 被 空格替换了 text "boot_1__normal/ " print(text.strip()"boot_1__normal/") # 打印不可见字符 print(repr(text))>>> False boot_1__normal/\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0…

[HGAME 2023 week1]Classic Childhood Game js前端查看

记录一下 给了我们一个游戏 按照web的尿性 都是要通关或者干嘛才可以 我们现在去看看js 这里发现结局的函数 window.setTimeout(function () {Message ["[Hero]玩家&#xff0c;恭喜你&#xff01;通关普通结局的纪元魔塔。", "[Npc3,仙子]谢谢支持&#xff…

私募机构全球化视野探析 | 活动回顾

10月21日&#xff0c;华锐技术、国泰君安证券和亚马逊云科技在深圳联合举办了一场主题为“私募机构全球化视野探析”的交流活动&#xff0c;广深及周边地区数十家机构近百人参会。本次活动众多行业大咖和专家从服务、技术、数据、资金等多个角度&#xff0c;共同探讨私募机构全…

【高效开发工具系列】你真的会使用Mac吗?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

leetcode第369周赛

2917. 找出数组中的 K-or 值 给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。 nums 中的 K-or 是一个满足以下条件的非负整数&#xff1a; 只有在 nums 中&#xff0c;至少存在 k 个元素的第 i 位值为 1 &#xff0c;那么 K-or 中的第 i 位的值才是 1 。 返回 nums …

旅行社信息展示服务预约小程序的作用是什么

出行旅游近些年人次非常多&#xff0c;除了自己出行外&#xff0c;旅行社成为众多人的选择&#xff0c;而随着消费者线上信息获取度增加&#xff0c;因此对商家来说也需要线上发展实现赋能。 那么通过【雨科】平台做个旅行社小程序有什么效果呢&#xff1f; 1、品牌宣传、内容…

解决深度学习训练时使用tensorboard http://localhost:6006/无法访问此网站问题

在windows上跑yolov5模型使用了Tensorboard来查看训练过程&#xff0c;开始训练&#xff0c;终端就会提示 直接点击这个网址&#xff0c;就会出现 解决办法是重新开一个终端&#xff0c;激活目前正在使用的虚拟环境&#xff0c;在下面输入 tensorboard --logdir runs\train -…

项目赶工期,如何预防团队成员任务冲突?

项目赶工期时&#xff0c;如果发生任务冲突&#xff0c;往往会直接影响工作进度和效率&#xff0c;可能会导致任务的延误或错失关键节点&#xff0c;进而影响整个项目进度。因此预防团队成员任务冲突对于项目进度至关重要。它可以提高工作效率&#xff0c;保证项目进度&#xf…

Linux中的lrzsz 玩法

一、介绍 lrzsz是一款在Linux里可代替ftp上传和下载的程序&#xff0c;也就是一款软件。它是开发者常用的一款工具&#xff0c;这个工具用于windows机器和远端的Linux机器通过XShell传输文件。 二、lrzsz的安装 在安装之前&#xff0c;我们可以使用下述命令先查看yum仓库中是否…

cad怎么转换成pdf?

cad怎么转换成pdf&#xff1f;cad是什么格式&#xff1f;CAD是计算机辅助设计&#xff08;Computer-Aided Design&#xff09;的缩写&#xff0c;是一种用于制图和设计的软件。CAD软件可以帮助工程师、建筑师、设计师等专业人士创建和编辑各种类型的图形和设计&#xff0c;如平…

行云创新加入深圳市人工智能行业协会

近日&#xff0c;行云创新正式加入深圳市人工智能行业协会。标志着行云创新在人工智能领域的实力和影响力得到了市场更加广泛和深入的认可&#xff0c;展示了行云创新对于深入参与人工智能行业发展&#xff0c;以及与其他领先企业共同推动中国人工智能技术进步的坚定决心。 行…

leetcode:88. 合并两个有序数组(python3解法)

难度&#xff1a;简单 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&#xff1a;最终&am…

HBuilderX实现安卓真机调试

1. 简介 HBuilderX 简称 HX&#xff0c;HBuilder&#xff0c;H 是 HTML 的缩写&#xff0c;Builder 是建设者。是为前端开发者服务的通用 IDE&#xff0c;或者称为编辑器。与 vscode、sublime、webstorm 类似。 它可以开发普通 web 项目&#xff0c;也可以开发 DCloud 出品的 u…

PowerDesigner 16数据库(mysql)逆向生成pdm

1、配置数据源 2、测试数据源 but~~~~没成功&#xff0c;shift

KaiwuDB 亮相第四届跨国公司领导人青岛峰会

10月10日至12日&#xff0c;由商务部和山东省人民政府共同主办的第四届跨国公司领导人青岛峰会在青岛国际会议中心举办。该峰会为跨国公司打造的国家级开放平台&#xff0c;是聚集跨国公司与中国合作、专注跨国公司议题、分享跨国公司经验、链接资源、促进合作的重大活动。Kaiw…

香港高端人才通行证计划学校名单更新扩容184所大全!

香港高端人才通行证计划学校名单更新扩容184所大全&#xff01; 近日香港特首在《施政报告》中宣布&#xff0c;将可以直接申请高端人才通行证计划B、C类的“世界百强名校”名单从176所增加到184所。 因为目前高才通是申请香港身份最快捷的途径&#xff0c;具有申请简单、审批迅…