I/O多路复用使得程序能同时监听多个文件描述符能够提高程序的性能
原理:借助内核,select来监听,客户端连接、数据通信事件。
select原型: int select (int maxfd + 1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval * timeout); 参数一:最大的文件描述符加1。 参数二:用于检查可读性, 参数三:用于检查可写性, 参数四:用于检查带外数据, 参数五:一个指向timeval结构的指针,用于决定select等待I/o的最长时间。如果为空将一直等待。t select: void FD_ZERO(fd_set*set); ---清空一个文件描述符集合。 fd_set rset; FD_ZERO(&rset); void FD_SET(int fd, fd_set *set); ---将一个文件描述符从监听集合 移除。 FD_CLR(4, &rset); int FD_ISSET(int fd,fd_set *set); ---判断一个文件描述符是否在监听集合中。 返回值:在:1;不在:0; FD_ISSET(4, &rset); int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); nfds:监听的所有文件描述符中,最大文件描述符+1 readfds:读文件描述符监听集合。 传入、传出参数 writefds:写文件描述符监听集合。 传入、传出参数 NULL exceptfds:异常 文件描述符监听集合 传入、传出参数 NULL timeout: > 0: 设置监听超时时长。 NULL:阻塞监听 0: 非阻塞监听,轮询 返回值: >0:所有监听集合(3个)中,满足对应事件的总数。 0: 没有满足监听条件的文件描述符 -1: errno 思路分析: lfd=socket(); 创建套接字 bind(); 绑定地址结构 listen(); 设置监听上限 fd_set rset, allset; 创建r监听集合 FD_ZERO(&allset); 将r监听集合清空 FD_SET(lfd,&allset); 将lfd添加至读集合中。 while(1){ rset=allset; 保存监听集合 ret=select(lfd+1,&rset, NULL,NULL,NULL); 监听文件描述符集合对应事件。 if(ret>0){ 有监听的描述符满足对应事件 if(FD_ISSET(lfd,&rset)) { //1在。0不在 cfd=accept(); 建立连接,返回用于通信的文件描述符 maxfd=cfd; FD_SET(cfd,&allset); 添加到监听通信描述符集合中。 } for(i=lfd+1;i<=最大文件描述符;i++){ FD_ISSET(i,&rset) 有read、write事件 read() 小--大 write(); } } }select优缺点:
缺点:监听上限受文件描述符限制。最大 1024
检测满足条件的fd,自己添加业务逻辑提高小。提高了编码难度。
优点:跨平台。win、Linux、macOS、Unix、mips
pollpoll: int poll(struct pollfd *fds, nfds_t nfds, int timeout); fds:监听的文件描述符【数组】 struct pollfd{ int fd: 待监听的文件描述符 short events: 待监听的文件描述符对应的监听事件 取值:POLLIN、POLLOUT、POLLERR short revnets: 传入时,给0。如果满足对应事件的话,返回 非0---》POLLIN、POLLOUT、POLLERR } nfds:监听数组的,实际有效监听个数。 timeout:> 0:超时时长。 单位:毫秒。 -1:阻塞等待 0:不阻塞 返回值:返回满足对应监听事件的文件描述符 总个数。 优点: 自带数组结构。将监听事件集合 和返回事件集合 分离。 拓展 监听上限。 超出1024限制。 缺点: 不能跨平台。Linux 无法直接定位满足监听事件的文件描述符, 编码难度较大。read函数
read函数返回值: > 0:实际读到的字节数 = 0:socket中,表示对端关闭。close() -1:如果errno==EINTR 被异常终端。需要重启。 如果errno==EAGIN或EWOULDBLOCK以非阻塞方式读数据,但是没有数据。需要,再次读。 如果errno==EConNRESET 说明连接被 重置。需要close( ),移除监听队列。 错误。 突破1024文件描述符限制: cat /proc/sys/fs/file-max -->当前计算机能打开的最大文件个数。受硬件影响。 ulimit -a ——>当前用户下的进程,默认打开文件描述符个数。缺省为1024 修改: 打开sudo vi /etc/security/limits.conf,写入: * soft nofile 65536 ——>设置默认值,可以直接借助命令修改。【注销用户,使其生效】 * hard nofile 100000 ——>命令修改上限。epoll
epoll: int epoll_create(int size); 创建一棵监听红黑树 size:创建的红黑树的监听节点数量。(仅供内核参考。) 返回值:指向新创建的红黑树的根节点的fd。 失败:-1 errno int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); *** 作监听红黑树 epfd:epoll_create 函数的返回值。 epfd op:对该监听红黑树所做的 *** 作。 EPOLL_CTL_ADD 添加fd到 监听红黑树 EPOLL_CTL_MOD 修改fd在监听红黑树上的监听事件。 EPOLL_CTL_DEL 将一个fd从监听红黑树上摘下(取消监听) fd: 待监听的fd event:本质 struct epoll_event 结构体 地址 成员 events: EPOLLIN/EPOLLOUT/EPOLLERR 成员 data:联合体: int fd; 对应监听事件的fd void *ptr; uint32_t u32; uint64_t u64; 返回值:成功 0;失败:-1 errno int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); 阻塞监听 epfd:epoll_create 函数的返回值。 epfd events:传出参数,【数组】,满足监听条件的哪些fd结构体。 maxevents:数组 元素的总个数。 1024 struct epoll_event evnets[1024]; timeout: -1:阻塞 0: 不阻塞 >0:超出时间(毫秒) 返回值: >0:满足监听的总个数。 可以用作循环上限。 0:没有fd满足监听事件 -1:失败。 errnoepoll实现多路IO转接思路
lfd=socket(); 监听连接事件lfd bind(); listen(); int epfd=epoll_create(1024); epfd,监听红黑树的树根。 struct epoll_event tep, ep[1024]; tep,用来设置单个fd属性,ep是epoll_wait()传出的满足监听事件的数组。 tep.events=EPOLLIN; tep.data.fd=lfd epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&tep); 将lfd添加到监听红黑树上。 while(1){ ret=epoll_wait(epfd,ep,1024,-1); 实施监听 for(i=0;i0){ 小--大 write(ep[i].data.fd,buf,n) } } } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)