Linux29 -- io复用方法:select、poll、epoll
创始人
2024-06-03 11:10:42
0

一、io复用方法:select、poll、epoll

一段时间内,轮询检测描述符上有没有用户感兴趣的事件产生。
描述符:socket、管道、键盘等
事件 :读、写、异常

对于套接字来说,

在这里插入图片描述

集合操作

FD_ZER(&fdset) fd_set – 清空整个集合,将fd_set 内的所有位置为0
FD_SET(fd,&fdest) fd ->fd_set – 向集合中添加描述符,将一个描述符添加到对应集合 fd_set
FD ISSET(fd,&fdset) – 检测描述符上是否有事件,检测描述符在集合中对应位置是否被置为1

fdset: 0000 0000
加入1,3,5
fdset: 0101 0100
select(&fdset) - 0001 0000

使用select 去检查服务器端的描述符 sockfd,c1,c2 …

二、select

select系统调用的用途是:

在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常等事件。

在这里插入图片描述

和tcp服务器端结合,单线程的一个程序,同时处理多个客户端

思路:
先将描述符收集起来,再将数组中的描述符添加到集合中;
在这里插入图片描述

代码select:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define MAXFD 10void fds_init(int fds[])//对数组进行初始化
{for(int i=0; ifds[i] = -1;}
}void fds_add(int fd, int fds[])
{for(int i=0; iif(fds[i] == -1)//表示空闲,没有人使用{fds[i] = fd;break;}}
}int accept_client(int sockfd)
{struct sockaddr_in caddr;int len = sizeof(caddr);int c = accept(sockfd,(struct sockaddr*)&caddr,&len);return c;
}int socket_init();void fds_del(int fd,int fds[])//删除元素
{for(int i = 0; i < MAXFD; i++){if(fds[i] == fd){fds[i] = -1;break;}}
}int main()
{int fds[MAXFD];//记录所有的描述符fds_init(fds);int sockfd = socket_init();//监听套接字if( sockfd == -1){exit(0);}fds_add(sockfd,fds);//添加fd_set fdset;//集合收集描述符while( 1 ){FD_ZERO(&fdset);int maxfd = -1;//利用该找出集合中最大的描述符for(int i=0; iif( fds[i] == -1 ){continue;}FD_SET(fds[i],&fdset);//将数组中有效的描述符添加到集合if( fds[i] > maxfd ){maxfd = fds[i];}}struct timeval tv = {5,0};int n = select(maxfd+1,&fdset,NULL,NULL,&tv);//阻塞if( n == -1 ){printf("select err\n");}else if( n == 0){printf("time out\n");}else{for(int i = 0; i < MAXFD ; i++){if(fds[i] == -1){continue;}if( FD_ISSET(fds[i],&fdset))//判断描述符上是否有数据{if( fds[i] == sockfd)//为监听套接字,accept{int c = accept_client(fds[i]);if(c != -1){fds_add(c,fds);//添加新接收的连接}           }else//recv{char buff[128] = {0};int num = recv(fds[i],buff,127,0);if(num <= 0)//出错或对方关闭{close(fds[i]);//关闭fds_del(fds[i],fds);//移除}else{printf("recv:%s\n",buff);send(fds[i],"ok",2,0);}}}}}}
}int socket_init()
{int sockfd = socket(AF_INET,SOCK_STREAM,0);if( sockfd == -1){return -1;}struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if( res == -1){ printf("bind err\n");return -1;}res = listen(sockfd,5);if(res == -1){return -1;}return sockfd;
}

运行结果:

在这里插入图片描述

int num = recv(fds[i],buff,127,0);

在这里插入图片描述
在这里插入图片描述
还有一个ok

三、poll

poll类似于select,但它相当于加强版的select,其支持的小事件类型要更多。可以接收更多的描述符,select默认为1024,poll的则是根据所给数组所能容纳的大小。
即,poll实现类型更多,能容纳的描述符更多。内核所实现的效率相同。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

事件设计:

某个位置为1,则代表某一事件。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现示例:一个进程实现服务器端支持多个客户端连接

#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define MAXFD   10int socket_init();
void fds_del(int fd, struct pollfd fds[]);
int accept_client(int sockfd)
{struct sockaddr_in caddr;int len = sizeof(caddr);int c = accept(sockfd,(struct sockaddr*)&caddr,&len);return c;
}void recv_data(int c, struct pollfd fds[])
{char buff[128] = {0};int n = recv(c,buff,1,0);if ( n <= 0 ){close(c);fds_del(c,fds);printf("client close\n");return;}printf("recv:%s\n",buff);send(c,"ok",2,0);
}
void fds_init(struct pollfd fds[])
{for(int i = 0; i < MAXFD; i++ ){fds[i].fd = -1;fds[i].events = 0;fds[i].revents = 0;}
}void fds_add(int fd, struct pollfd fds[])
{for( int i = 0; i < MAXFD; i++ ){if ( fds[i].fd == -1 ){fds[i].fd = fd;fds[i].events = POLLIN | POLLRDHUP;fds[i].revents = 0;break;}}
}void fds_del(int fd, struct pollfd fds[])
{for(int i = 0; i < MAXFD; i++ ){if ( fds[i].fd == fd){fds[i].fd = -1;fds[i].events = 0;fds[i].revents = 0;break;}}
}
int main()
{int sockfd = socket_init();if ( sockfd == -1 ){exit(0);}struct pollfd fds[MAXFD];fds_init(fds);fds_add(sockfd,fds);//sockfd -> fdswhile( 1 ){int n = poll(fds,MAXFD,5000);//阻塞if ( n == -1 ){printf("poll err\n");}else if ( n == 0 ){printf("time out\n");}else{for(int i = 0; i < MAXFD; i++ ){if( fds[i].fd == -1 ){continue;}/*if ( fds[i].revents & POLLRDHUP){printf("close --rdhup\n");close(fds[i].fd);fds_del(fds[i].fd,fds);continue;}*/if ( fds[i].revents & POLLIN ){if ( fds[i].fd == sockfd){int c = accept_client(sockfd);if ( c != -1 ){fds_add(c,fds);}}else{recv_data(fds[i].fd,fds);}}//if ( fds[i].revents & POLLOUT )//{//}}}}}int socket_init()
{int sockfd = socket(AF_INET,SOCK_STREAM,0);if ( sockfd == -1 ){return -1;}struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if ( res == -1 ){printf("bind err\n");return -1;}res = listen(sockfd,5);if ( res == -1 ){return -1;}return sockfd;
}

运行结果

在这里插入图片描述

四、epoll

是Linux系统上特有的系统调用。
epoll 其实是由一组方法构成。

epoll_create() //在内核中创建内核事件表 -- 相当于容器 ,是一颗红黑树,RBR
epoll_ctl() //向表中添加,修改,移除
epoll_wait() //获取就绪描述符

在这里插入图片描述
在这里插入图片描述

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...