IO多路转接模型:select/poll/epoll

对大量描述符进行事件监控(可读/可写/异常)

select模型

  1. 用户定义描述符的事件监控集合 fd_set(这是一个位图,用于存储要监控的描述符); 用户将需要监控的描述符添加到集合中,这个描述符集合的大小取决于一个宏 _FD_SETSIZE = 1024
  2. 将集合拷贝到内核中进行监控;在内核中对所有描述符进行轮询遍历判断是否有关心的事件就绪
  3. 若有描述符就绪,从监控集合中将未就绪的描述符移除;然后调用返回(返回给用户就绪描述符饥集合)
  4. 用户遍历所有描述符,判断描述符是否在集合中,若在集合中,则这个描述符是就绪描述符
  5. 用户针对这个就绪的描述符事件进行相应的处理,用户仅仅对大量描述符中就绪的描述符进行处理,sock程序就可以避免accept/recv处因为没有数据到来而阻塞
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h> int select(int nfds, fd_set *readfds,fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  • nfds: 监控的文件描述符集里最大文件描述符加1,因为此参数会告诉内核检测前多少个文件描述符的状态
  • readfds: 监控有读数据到达文件描述符集合,传入传出参数
  • writefds: 监控写数据到达文件描述符集合,传入传出参数
  • exceptfds: 监控异常发生达文件描述符集合,如带外数据到达异常,传入传出参数
  • timeout: 定时阻塞监控时间,3种情况
    1. NULL,永远等下去
    2. 设置timeval,等待固定时间
    3. 设置timeval里时间均为0,检查描述字后立即返回,轮询
struct timeval
{
long tv_sec; /* seconds */ //秒
long tv_usec; /* microseconds */ //微秒
};
  • void FD_CLR(int fd, fd_set *set); //把文件描述符集合里fd清0 将指定的描述符从集合中移除
  • int FD_ISSET(int fd, fd_set *set); //测试文件描述符集合里fd是否置1 判断指定的描述符是否在集合中
  • void FD_SET(int fd, fd_set *set); //把文件描述符集合里fd位置1 将指定的描述符添加到集合中
  • void FD_ZERO(fd_set *set); //把文件描述符集合里所有位清0 清空描述符集合

select优缺点

  1. select 能监听的文件描述符个数受限于 FD_SETSIZE,一般为 1024,单纯改变进程打开的文件描述符个数并不 能改变 select 监听文件个数
  2. 每次都需要重新将监控集合拷贝到内核(select会修改集合)
  3. 解决 1024 以下客户端时使用 select 是很合适的,但如果链接客户端过多,select 采用的是轮询模型,会大大降低服务器响应效率
  4. select返回给用户就绪的描述符集合(将未就绪的描述符从集合中移除),但是并没有告诉用户具体哪一个描述符就绪,需要用户遍历描述符是否在集合中来判断哪个描述符就绪,这个判断是一个遍历的过程,性能随着描述符增多而下降,并且复杂度更高
  5. select每次返回都会修改监控集合,因此每次都需要用户重新向集合中添加所有描述符
  6. select遵循posix标准,支持跨平台;
  7. 监控的超时等待时间可以精细到微秒
class Select
{public:Add(TcpSocket &sock); //将用户关心socket描述符添加到监控集合中Del(TcpSocket &sock); //从监控集合中移除不再关心的socket描述符Wait(std::vector<TcpSocket>&list,init timeout_sec,int timeout_sec); //从开始监控,并且向用户返回就绪的socket
private:fd_set _rfds;int_max_fd;
};

实现

select服务端

/** 这个文件封装一个select类,向外界提供更加简单点的select监控接口*  将用户关心socket描述符添加到监控集合中* 从监控集合中移除不再关心的socket描述符* 从开始监控,并且向用户返回就绪的socket*/#include<vector>
#include<sys/select.h>
#include"tcpsocket.hpp"class Select
{public:Select(): _max_fd (-1){FD_ZERO(&_rfds);//清空集合}   bool Add(TcpSocket &sock){int fd = sock.GetFd();//void FD_SET(int fd,fd_set *set)//向set描述符集合中添加fd描述符FD_SET(fd,&_rfds);_max_fd = _max_fd > fd ? _max_fd : fd; return true;}   bool Del(TcpSocket &sock){int fd = sock.GetFd();//void FD_CLR(int fd, fd_set *set)//从set描述符集合中移除FD_CLR(fd,&_rfds);//从最大的往前遍历for(int i = _max_fd ; i >= 0; i--){//int FD_ISSET(int fd, fd_set *set);//判断fd描述符是否还在set集合中if(FD_ISSET(i,&_rfds)){_max_fd = i;break;}}} bool Wait(std::vector<TcpSocket>&list,int timeout_sec = 3){struct timeval tv; //超时时间tv.tv_sec = timeout_sec;tv.tv_usec = 0;fd_set set = _rfds;int ret =select(_max_fd + 1, &set, NULL ,NULL,&tv);if(ret < 0){perror("select error");return false;}else if(ret == 0){std::cout<< "select wait timeout\n";return false;}for(int i =0 ;i <= _max_fd; i++){if(FD_ISSET(i,&set)){TcpSocket sock;sock.SetFd(i);list.push_back(sock);}}return true;} private:fd_set _rfds;int _max_fd;
};int main()
{TcpSocket sock;CHECK_RET(sock.Socket());CHECK_RET(sock.Bind("192.168.145.132",9000));CHECK_RET(sock.Listen());Select s;s.Add(sock);                                                                while(1){std::vector<TcpSocket>list;if(s.Wait(list)==false){continue;}for(int i =0 ; i < list.size(); i++){//判读socket是监听socket还是通信socketif(list[i].GetFd() == sock.GetFd()){TcpSocket clisock;std::string cli_ip;uint16_t cli_port;if(sock.Accept(clisock,cli_ip,cli_port) == false){continue;}s.Add(clisock);}else{std::string buf;if(list[i].Recv(buf) == false){s.Del(list[i]);list[i].Close();continue;}std::cout<<"client say:"<<buf <"\n";}}}sock.Close();return 0;
}

客户端


#include <signal.h>
#include "tcpsocket.hpp"void sigcb(int signo){printf("connection closed\n");
}
int main(int argc, char *argv[])
{if (argc != 3) {std::cout<<"./tcp_cli 192.168.122.132 9000\n";return -1; }   std::string ip = argv[1];uint16_t port = atoi(argv[2]);signal(SIGPIPE, sigcb);TcpSocket sock;CHECK_RET(sock.Socket());CHECK_RET(sock.Connect(ip, port));while(1) {std::string buf;std::cout <<"client say:";fflush(stdout);std::cin >>buf;sock.Send(buf);}sock.Close();return 0;
}



poll模型

poll函数接口

#include <poll.h>int poll(struct pollfd *fds, nfds_t nfds, int timeout);// pollfd结构
struct pollfd {  int   fd;         /* 用户监控的描述符 */   short events;     /* 描述符关心的事件 POLLIN/POLLOUT */  short revents;    /* 描述符实际就绪的事件 */ }
参数说明
  • fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合. 描述事件结构数组
  • nfds表示fds数组的长度. 要监控事件个数
  • timeout表示poll函数的超时时间, 单位是毫秒(ms).
events和revents的取值:
  1. POLLIN 普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND
  2. POLLRDNORM 数据可读
  3. POLLRDBAND 优先级带数据可读
  4. POLLPRI 高优先级可读数据
  5. POLLOUT 普通或带外数据可写
  6. POLLWRNORM 数据可写
  7. POLLWRBAND 优先级带数据可写
  8. POLLERR 发生错误
  9. POLLHUP 发生挂起
  10. POLLNVAL 描述字不是一个打开的文件

实现原理

  1. 用户定义描述符事件数组,向数组中添加关心的描述符事件
  2. 将pollfd事件数组,拷贝到内核中进行遍历轮询监控,判断是否就绪了关心的事件
  3. 将描述符实际就绪的事件信息,标记到revents中
  4. 当poll返回。用户遍历pollfd事件数组,通过revents判断描述符就绪了什么事件,进而进行相应操作

使用poll监控标准输入

#include <poll.h>
#include <unistd.h>
#include <stdio.h>int main() { struct pollfd poll_fd;  //一个结构就是一个事件poll_fd.fd = 0;  poll_fd.events = POLLIN;  //可读事件for (;;) {    //开始监控int ret = poll(&poll_fd, 1, 1000); //遍历轮询   if (ret < 0) {    //出错perror("poll");    continue;    }if (ret == 0) {      //超时printf("poll timeout\n");     continue;   } //ret>0if (poll_fd.revents == POLLIN) {  //看事件是否为我们所关心的事件 //对事件进行操作    char buf[1024] = {0};      read(0, buf, sizeof(buf) - 1);     printf("stdin:%s", buf);    }  }
}

poll有缺点分析

优点:
  1. poll采用事件结构形式对描述符关心的事件进行监控,简化了select三种集合操作的流程
  2. poll没有描述符上限的设置
缺点
  1. 不能跨平台,只能用于Linux下
  2. 在内核中进行轮询遍历判断就绪,性能随着描述符事件增多而下降
  3. 也不会告诉用户具体哪一个描述符就绪,需要用户轮询遍历判断事件中的revents;进而对描述符进行相应事件操作 revents & POLLIN/POLLOUT
  4. 需要每次都向内核中拷贝监控信息

IO多路转接模型----(select的模型,select的优缺点,poll的模型,poll的优缺点)相关推荐

  1. epoll(eventpoll)是干嘛的?IO多路转接技术(相较select、poll的优点)

    首先我们要知道epoll是用来干什么的(定义):epoll是一种IO多路转接技术,在LINUX网络编程中,经常用来做事件触发,即当有特定事件到来时,能够检测到,而不必阻塞进行监听,基于事件驱动的IO多 ...

  2. IO多路转接模型-----epoll

    epoll: Linux下性能最高的多路转接模型 epoll 有3个相关的系统调用. epoll_create 功能:创建epoll,在内核中创建eventpoll结构体,size决定了epoll最多 ...

  3. IO多路转接 ——— select、poll、epoll

    文章目录 I/O多路转接之select select初识 select函数 socket就绪条件 select基本工作流程 select服务器 select的优点 select的缺点 select的适 ...

  4. select poll epoll 高效IO 多路转接

    目录 五种常见IO模型 高效IO的概念 阻塞 vs 非阻塞 非阻塞IO fcntl函数 I/O多路转接之select 初识select select函数原型 select操作接口 tcp_server ...

  5. 网络编程(三)TCP IO多路转接服务器编程(select)

    同系列文章: 1, 文章目录 一,select知识引入 二,select基本概念 2.1 select概念理解前先总结一下通信过程(便于后文理解select) 2.2 select函数的用法 2.2. ...

  6. IO多路转接之epoll

    IO多路转接之epoll 文章目录 IO多路转接之epoll 一.epool 二.基于epoll实现服务器(LT) 三.**基于epoll实现服务器(LT)** 一.epool 是为处理大批量句柄而作 ...

  7. 高级IO--1 ---(五种典型IO,阻塞IO,非阻塞IO,信号驱动IO,异步IO, IO多路转接)

    高级IO: 五种典型IO: 阻塞IO/非阻塞IO/信号驱动IO/异步IO/IO多路转接 IO多路转接模型:select/poll/epoll 五种典型IO 阻塞IO IO操作的流程:等待IO操作条件具 ...

  8. IO多路转接之poll

    IO多路转接之poll 文章目录 IO多路转接之poll 一.Poll 一.Poll 1.函数原型: int poll(struct pollfd *fds, nfds_t nfds, int tim ...

  9. Linux C语言IO多路转接select函数

    动态数组的实现与前面代码相同,下面是主函数 #include <netdb.h> #include <stdio.h> #include <sys/socket.h> ...

最新文章

  1. 深度学习论文阅读路线图
  2. 盛辉智能机器人安全吗_蓝小欧车载安全机器人构建全方位智能出行安全生态
  3. python处理svg 平移 旋转_d3.js封装文本实现自动换行和旋转平移等功能
  4. Android 开机自动运行和添加删除桌面快捷方式
  5. ELK安装配置及nginx日志分析
  6. 国贸专业要考计算机二级,国贸专业考计算机二级有没有必要
  7. android开启热点softap模式,[RK3288][Android6.0] Wifi开启热点(SoftAP)流程小结
  8. Web Api 内部数据思考 和 利用http缓存优化 Api
  9. “手把手撕LeetCode题目,扒各种算法套路的裤子”
  10. DB and RAC(11.2.0.3 ) Patch Set Update(11.2.0.3.6 )
  11. 《Excel与VBA程序设计》第四章新鲜出炉
  12. 商城系统订单超时自动取消解决方案
  13. 腾讯云学生服务器搭建个人网站——配置web开发环境详细步骤
  14. 运维工程师故障排查思路(备忘)
  15. 一款强大的网站在线客服聊天系统:whisper搭建教程
  16. 法语语法学习笔记——代词(1)
  17. 511遇见易语言文本替换和子文本替换
  18. oracle-1480,1400,1438错误一些解决方式
  19. 典型Π型RC滤波电路
  20. 好分数学生家长查排名成绩-在线好分数查成绩入口

热门文章

  1. Struts2中数据封装方式
  2. 《JavaScript权威指南》学习笔记 第二天 下好一盘大棋
  3. 中国寒龙反网络病毒联盟核心小组:官方公告,近期本站将会发布各种编程技术视频教程,详情请点击我们的以下公告!...
  4. 《计算机组成与体系结构:性能设计》读后小记 6、外部存储器
  5. 【7】jQuery学习——入门jQuery选择器之过滤选择器-可见性过滤选择器
  6. vue.js根据数据循环生成表格_vue.js循环for(列表渲染)详解
  7. 按小时分组mysql 补齐_分组记录按小时或按天白天和mysql的
  8. 引导界面图标好大_游戏里那些图标和界面,原来是这么设计出来的?
  9. windows无法连接到某个wifi_windows无法连接到无线网络怎么办【解决方法】
  10. java 自定义对象 排序,使用自定义排序顺序对对象的ArrayList进行排序