首页 > 编程语言 >网络编程相关(IO多路复用)

网络编程相关(IO多路复用)

时间:2023-06-16 21:23:10浏览次数:37  
标签:多路复用 epoll int 编程 fd 事件 IO include event

select poll epoll的一些比较

select的fd_set通过bitmap1024位表示存入的文件描述符,通过01表示存入的文佳描述符,且是从0下标开始,如存入的文件描述符是12579,则在bitmap里表示是0110010101000...

由于bitmap从0下标开始,存入的文件描述符从1开始,从0到存入的最大文件描述符,范围是max+1

select主要是把bitmap从用户态拷贝到内核态,由内核态来判断,没有数据变化,select会阻塞,有变化,bitmap的fd会被置位

fd_set不可重用


poll

struct pollfd
{
int fd;
short events;//事件
short revents;//有变化被置位,什么事件,置为什么事件,最后要将revents重新置为0
}

epoll句柄,用户态核内核态共享

select

关于select,主要是把服务端的根sockfd也加入到fd_set,FD_SET(listenSocket,&socketSet); readSet=socketSet;
writeSet=socketSet; 把加入的fd描述符更新到读写set中

FD_ZERO(&socketSet);
//将文件描述符放入 socketSet,
//用于accept  
FD_SET(listenSocket,&socketSet); 
//统计最大的socket 
int maxfd = listenSocket;
int conNum = 1;
//数组存储连接的socket
int connectArray[1024]={0};
while(true)  
{  
    //清空读写集合
    FD_ZERO(&readSet);  
    FD_ZERO(&writeSet);
    //读写都监听
    readSet=socketSet;  
    writeSet=socketSet;

有新连接时:FD_SET(connectArray[conNum],&socketSet);放到socketSet后面while又更新到read_set

if(FD_ISSET(listenSocket,&readSet))
    {  
        acceptSocket=accept(listenSocket,(sockaddr*)&addr,&len);  
        if(acceptSocket==INVALID_SOCKET)  
        {  
            return false;  
        }  
        else  
        {  
            //大于我们最大的监听数量了
            if(conNum > 1024)
            {
                return false;
            }
            //更新数组
            connectArray[conNum] = acceptSocket;
            //设置非阻塞
            fcntl(connectArray[conNum], F_SETFL, O_NONBLOCK );
            //加到socketset里,以后赋值给读写集合
            FD_SET(connectArray[conNum],&socketSet);
            if(acceptSocket > maxfd)
            {
                maxfd = acceptSocket;
            }
            conNum++;

poll

poll与select不同在于:poll将要监听的对象存到数组中,且数组中的元素是结构体(存fd,要监听的事件,返回监听到的事件)

epoll

epoll查找基于红黑树,返回已经变化的文件描述符

一开始就监听服务端的fd(把服务端的fd事件加到epoll的监听树上(水平模式,只要有连接就通知服务端读事件)(监听读事件)),如果有客户端要建立连接,会触发epoll读事件

epoll_wait()接着通过for循环判断读事件结构体事件的fd是否是服务器的fd,若是就要建立连接的客户端数量:通过accept()接收客户端的连接,创建与客户端连接的sockfd,(边缘模式,非阻塞,加入epoll树,)epoll只是通知有事件要处理,具体处理要具体实现。;;若是触发epoll读事件的是要与客户端通信的sockfd,则在epoll_wait()数量的for里判断,进行通信

epoll边缘模式中,有要建立连接的客户端请求,将建立连接的sockfd1的结构体的事件设为读事件同时为边缘模式

connfd=accept(...);
event.data.fd = connfd;
event.events = EPOLLIN |EPOLLET;//读与边缘(服务端与客户端通信的fd的事件)
epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event);

若是在已建立连接的客户端与服务端进行通信,由于服务端的sockfd是边缘模式,要一次性读完;同时读完后,服务端要与该客户端进行通信的connfd改为写事件(读完客户端的信息后,要进行回应,改为写边缘),服务端有数据发送回客户端触发写事件,一次性写完。

//更改为写模式
event.data.fd = connfd;
event.events = EPOLLOUT | EPOLLET;
//EPOLL_CTL_MOD,根据已有的connfd,修改事件结构体
epoll_ctl(epollfd, EPOLL_CTL_MOD, connfd, &event);

边缘模式下accept() do{accept(); 读完退出}while(1)要一次读完,直到返回值为<=0

边缘模式,写缓冲区满后,不写,contine 下一个epoll_event,下一次epoll_wait()写缓冲区变为非空后会触发写事件。

int nready = epoll_wait(epollfd, epoll_eventsList, eventsize, -1);
举一个例子假设epoll_event队列中有1000个文件描述符,第一次调用
epoll_wait返回5,那么表示队列前五个元素就绪了,如果不处理第三个就绪事件,其他的都处理。第二次调用epoll_wait会返回8,那么这8个epoll_event也是按顺序排列的。
所以认为epoll_wait这个函数做了内部的优化排序,返回给用户按顺序拍好的内存

epoll_wait最多返回eventsize个epoll_event

考虑把服务端的根sockfd设为水平模式,只要accept()里还有连接就触发,不用在do里全部读完accept()

epoll的一个示例:
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<vector>
#include<arpa/inet.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<cstring>
#include<unistd.h>
#include<sys/epoll.h>
#include<sys/socket.h>
#include<fcntl.h>
#include<memory.h>
#include<errno.h>
#define SIZE 20
using namespace std;

int main(int argc,char* argv[])
{
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_port=htons(atoi(argv[1]));
	serveraddr.sin_addr.s_addr=INADDR_ANY;

	if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0)
	{
		perror("bind error");
		exit(1);
	}

	if(listen(sockfd,128)<0)
	{
		perror("listen error");
		exit(1);
	}

	int epollfd=epoll_create(1);//创建epoll句柄

	struct epoll_event event;
	event.data.fd=sockfd;//将服务端的fd放到epoll中
	event.events=EPOLLIN;//水平读
	epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);//事件加入到epoll树

	vector<int> clientfds;
	struct epoll_event* ev=(struct epoll_event*)malloc(sizeof(struct epoll_event)*SIZE);//存epoll树返回的事件

	struct sockaddr_in client;
	int connfd;
	socklen_t len=sizeof(client);

	while(1)
	{
		int nearby=epoll_wait(epollfd,ev,SIZE,-1);//等待事件触发
		if(nearby==-1)//出错
		{
			if(errno==EINTR) continue;
		}
		if(nearby==0)//超时
		{
			continue;
		}
		for(int i=0;i<nearby;i++)
		{
			if(ev[i].data.fd==sockfd)//有新连接
			{
				connfd=accept(sockfd,(struct sockaddr*)&client,&len);
				char buff[1024]={0};
				inet_ntop(AF_INET,&client.sin_addr.s_addr,buff,sizeof(buff));
				cout<<"new connect:"<<buff<<endl;
				clientfds.push_back(connfd);
				event.data.fd=connfd;
				event.events=EPOLLIN|EPOLLET;//读加边缘模式,读完后改为写
				epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event);
			}else if(ev[i].events&EPOLLIN)//读事件
			{
				connfd=ev[i].data.fd;
				char buff[1024];
				int ret;
				do{
					memset(buff,0,sizeof(buff));
					ret=read(connfd,buff,sizeof(buff));
					if(ret<=0) break;//读完,退出
					puts(buff);
				}while(1);
				event.data.fd=connfd;
				event.events=EPOLLOUT|EPOLLET;//读完改为写
				epoll_ctl(epollfd,EPOLL_CTL_MOD,connfd,&event);
			}else if(ev[i].events&EPOLLOUT)//write
			{
				connfd=ev[i].data.fd;
                const char* buff="1234566788qqwersda";
                int ret;
                 do{
                   ret=write(connfd,buff,strlen(buff)+1);
                   if(ret<=0) break;//写完,退出
                 }while(1);
                 event.data.fd=connfd;
                 event.events=EPOLLIN|EPOLLET;//写完改为读
                 epoll_ctl(epollfd,EPOLL_CTL_MOD,connfd,&event);
		  }
		}
	}
}

epoll反应堆

epoll反应堆主要是读完数据后,改为写事件触发

主要是利用epoll_data的void*ptr该指针指向一个自定义结构体,该结构体存fd,events,回调函数

typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;

因此监听的对象的fd在自定义的结构体上

将void*ptr指向自定义的结构体的epoll_event上树,后面调用epoll_wait(),返回的 epoll_event结构体是在树上的结构体的复制体(将变化的节点拷贝下来)
.........

标签:多路复用,epoll,int,编程,fd,事件,IO,include,event
From: https://www.cnblogs.com/persistencejunjie/p/17486524.html

相关文章

  • java课设——《RookieSuperMario》【菜鸟版超级玛丽
    项目简介:我们团队利用面向对象开发方法和Javaswing框架,对经典游戏《SuperMario》进行编写。此项目共设施三个关卡,玩家可通过键盘来控制马里奥的移动,跳跃可以顶掉砖块,下落时还可以踩死蘑菇敌人,如果马里奥最终安全到达堡垒,则通关成功。个人项目负责任务: 创建背景类(BackGroun......
  • python入门学习之《python编程快速上手》
    #《python编程快速上手》1-9章第1-2章:python基础和控制流#python严格区分大小写;#代码行的缩进很重要,一般用4个空格。大多数情况下,代码行缩进告诉python它属于哪个代码块。#python下标从0开始;#行末使用续行字符\,将一行指令写成多行。在[],{},或()中的多行语句,不需要使用反斜......
  • boost asio库的一些记录(个人用)
    BOOSTasio#include<iostream>#include<boost/asio.hpp>#include<boost/date_time/posix_time/posix_time.hpp>intmain(){boost::asio::io_serviceio;//上下文,事件轮询处理框架(类似libevent的event_base)boost::asio::deadline_timert(io,boost::po......
  • springBoot 读取application.yml及优先级
    1.回顾之前的web.xml的加载方式  2.springBoot加载application.yml方式1.Application.run方法中的ConfigurableEnvironmentenvironment=this.prepareEnvironment(listeners,bootstrapContext,applicationArguments);是准备环境,里面会加载配置文件 2.prepareEnviron......
  • Vue进阶(幺贰陆):表格复用 TypeError: _self.$scopedSlots.default is not a function解
    (文章目录)一、前言在使用elementUI的el-table组件时,表头应用v-if判断来动态显示,正常来说这样的操作是没有问题的,但是如果在这基础上使用<templateslot-scope="scope">操作的话,表头一旦切换就会报错,错误信息如下:_self.$scopedSlots.defaultisnotafunction二、解决方......
  • 网络编程
    网络概述:多台相互连接的计算机资源共享    3.交换数据网络的类型分类按拓扑分类:星型结构 树型结构 总线型线程 环形结构 网状架构按范围分类:局域网LAN 城域网MAN 广域网WAN 补充:个人局域网PAN 互联网Internet按传输方式分类:有线网络 无线网络 网络......
  • axios headers设置发送接收文件类型
    接受blobexportfunctionexportTeacherActivitiesState(data:any):Promise<IResponse<Blob>>{ returnrequest({  baseURL,  url:"/Api/CourseEvaluationManage/ActivityStatistics/ExportTeacherActivitiesState",  method:"post......
  • 运行python -m uiautomator2 init报错AttributeError: module 'collections' has no a
    报错信息:Traceback(mostrecentcalllast):File"E:\Carte\BB\17-SiteLeadership\alte\IonelBalauta\Aryeht\Task1-Traducetotsite-ul\DoarGoogleWeb\Andreea\Meditatii\Sedinta9(2022)(EMAIL)\BEBE-PARSING-Python(fararedenumire2).p......
  •  iOS App 上架流程图文教学
    ​ iOSApp上架流程图文教学 在上架App之前必须先准备好开发者帐号,但申请开发者帐号因法兰克早在之前已经申请好了,故就跳过此步骤,直接从产生凭证到上传App开始讲起。首先,要将自己辛苦写好的App送审的话,则要依序做完下列几件事情即可。在开发者后台产生.cer(凭证档)......
  •  iOS App 上架流程图文教学
    ​ iOSApp上架流程图文教学 在上架App之前必须先准备好开发者帐号,但申请开发者帐号因法兰克早在之前已经申请好了,故就跳过此步骤,直接从产生凭证到上传App开始讲起。首先,要将自己辛苦写好的App送审的话,则要依序做完下列几件事情即可。在开发者后台产生.cer(凭证档)......