多路IO- poll

3.1简介

​ poll的机制与select类似,他们都是让内核在以线性的方法对文件描述符集合进行检测,根据描述符的状态进行具体的操作。并且poll和select在检测描述符集合时,会在检测的过程中频繁的进行用户区和内核区的拷贝,随着文件描述符集合中的数量增大,开销也随之增大,效率也会变低。

​ select检测的文件描述符数量最大为1024个,而poll则没有数量限制。并且select可以跨平台,而poll只能在Linux系统中使用

3.2 poll函数

头文件:#include <poll.h>

函数原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout);

函数参数:

​ fds:是一个struct pollfd类型的结构体数组,里面存储了要检测的文件描述符的信息,稍后详述。

​ nfds:数组实际有效内容的个数。

​ timeout:

	-1 一直阻塞,直到检测的集合中有就绪的文件描述符(发送事件的),然后解除阻塞。

	 0 不阻塞,不管检测集合中有没有已经就绪的文件描述符,函数会马上返回。

	大于0 阻塞指定的毫秒数之后,然后解除阻塞。

函数返回值:

大于0的整数:表示检测的集合中已就绪的文件描述符总个数。

-1:表示调用失败。

结构体struct pollfd的成员(三个):

int fd:委托内核检测的文件描述符。

short events:委托内核要检测文件描述符的事件。

short revents:文件描述符实际发送的事件,这是一个传出参数。

结构体struct pollfd事件表:

事件 常值 作为events的值 作为revents的值 说明
读事件 POLLIN 普通或优先带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级带数据可读
写事件 POLLOUT 普通或优先带数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
错误事件 POLLERR 发生错误
POLLHUP 发送挂起
POLLNVAL 描述不是打开的文件

3.3 使用poll实现高并发的步骤:

第一步:通过socket函数创建一个监听描述符。

第二步:通过bind函数将监听描述符和服务端本地地址进行绑定。

第三步:通过listen函数将监听文件描述符切换至监听状态。

第四步:自定义一个文件描述符集和结构体,然后对其初始化,然后准备一个变量用于记录最大描述符的值。

第五步:将监听文件描述符放入自定义文件描述集合下标为0的地方(不这样做的话集合为空,poll的第三参数是-1则会一直阻塞,如果是其他数则还是不行),然后进入无限循环

​ 使用poll函数检测刚才自定义的文件描述符集合,如果集合中的某个描述符有信号了(读,写,错误等),那么则根据poll的第三参数来判断是否阻塞。(因为集合第0个位置是监听符,所以如果监听符有动静代表有客户端连接服务端了)

​ 然后通过对监听描述符的读缓冲区进行判断,因为如果有客户端连接服务端,那么监听描述符的读缓存区就是这个客户端的信息,然后通过accept和监听文件描述符与刚才的客户端产生连接,返回一个与客户端产生连接的描述符,然后再拿这个与客户端建立连接的文件描述符存入自定义文件描述符集合供poll进行检测,加入之后记得跳出循环。

​ 之后我们判断现在已建立链接的文件描述符是否是最大的,如果是则记录一下。

​ 然后我们可以使用循环遍历的方法来遍历整个自定义文件描述符集合,来看看那个文件描述符是已建立链接的状态,然后通过使用这个文件描述符与对应的客户端进行数据传输,回应和处理客户端的各种请求等等。当回应完毕,处理完毕之后又或者客户端没有请求了,那么记得关闭这个与客户端建立连接的文件描述符,然后将这个文件描述符所在的自定义文件描述符集合中的位置置空,留给其他人使用。

第六步:可以不断重复第五步,如果所有的事情都做完了,那么可以关闭监听文件描述符。

3.4 使用poll实现高并发的服务端代码:

点击查看代码
#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <arpa/inet.h>

#include <sys/select.h>

#include <poll.h>

int main()

{

  int i;

  //1.创建套接字,使用ipv4和tcp

  int sfd = socket(AF_INET, SOCK_STREAM, 0);

  if (sfd == -1) {

​     printf("创建监听套接字失败!\n");

​     exit(0);

  }

  //2.绑定套接字

  struct sockaddr_in serv;

  serv.sin_family = AF_INET;

  serv.sin_port = htons(10066);

  serv.sin_addr.s_addr = INADDR_ANY;

  int rent = bind(sfd, (struct sockaddr*)&serv, sizeof(serv));

  if (rent == -1) {

​     printf("绑定套接字失败!\n");

​     exit(0);

  }

  //3.监听

  rent = listen(sfd, 128);

  if (rent == -1) {

​     printf("监听失败!\n");

​     exit(0);

  }

  //4.创建和初始化自定义文件描述符集合

  struct pollfd fds[1024];

  for (i = 0; i < 1024; i++) {

​     //初始化,-1代表这个位置为空,可用

​     fds[i].fd = -1;

​     fds[i].events = POLLIN;//设定事件

  }

  fds[0].fd = sfd;//自定义文件描述符集合第一个位置存放监听文件描述符

  int maxfd = 0;//用来记录最大的文件描述符

  //5.使用循环来等待链接。

  while (1) {

​     //委托内核检测

​     rent = poll(fds, maxfd + 1, -1);

​     if (rent == -1) {

​       printf("失败!\n");

​       exit(0);

​     }

​     

​     //代码执行到这里了代表有客户端的连接请求了

​     //因为集合的第一个位置是监听文件描述符,监听文件描述符有动静了

​     //代表有连接

​     if (fds[0].revents & POLLIN) {

​       //接收连接请求

​       struct sockaddr_in sockcli;

​       int len = sizeof(sockcli);

​       int connfd = accept(sfd, (struct sockaddr*)&sockcli, &len);

​       //将已建立连接的描述符交给内核来检测读缓冲区

​       for (i = 1; i < 1024; i++) {

​         if (fds[i].fd == -1) {

​           fds[i].fd = connfd;//将已连接的描述符加入检测

​           break;

​         }

​       }

​       //记录最大的值

​       maxfd = i > maxfd ? i : maxfd;

​       

​     }

​     //如果有客户端发送数据

​     int j;

​     for (j = 1; j <= maxfd; j++) {

​       if (fds[i].revents & POLLIN) {

​         char buf[100];

​         //读数据

​         int ret = read(fds[i].fd, buf, sizeof(buf));

​         if (ret == -1) {

​           printf("读取来自数据失败!\n");

​           exit(0);

​         }

​         else if (ret == 0) {

​           printf("对方已经关闭了连接!\n");

​           //关闭连接之后记得关闭对应的描述符,然后将自定义的集合中相应的位置值为-1

​           close(fds[i].fd);

​           fds[i].fd = -1;

​         }

​         else {

​           printf("来自客户端:%s\n",buf);

​           //回应客户端

​           write(fds[i].fd, buf, sizeof(buf));

​         }

​       }

​     }

  }

  close(sfd);

  return 0;

}

多路io复用pool [补档-2023-07-19]的更多相关文章

  1. Redis03——Redis之单线程+多路IO复用技术

    Redis 是单线程+多路IO复用技术 多路复用:使用一个线程来检查多个文件描述符的就绪状态 如果有一个文件描述符就绪,则返回 否则阻塞直到超时 得到就绪状态后进行真正的操作可以在同一个线程里执行,也 ...

  2. 多路IO复用模型--select, poll, epoll

    select 1.select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数 2.解决1024以下客户端时使用se ...

  3. Linux企业级项目实践之网络爬虫(27)——多路IO复用

    与多线程和多进程相比,I/O多路复用的最大优势是系统开销小,系统不需要建立新的进程或者线程,也不必维护这些线程和进程. 主要应用: (1)客户程序需要同时处理交互式的输入和服务器之间的网络连接 (2) ...

  4. 协程与多路io复用epool关系

    linux上其实底层都基于libevent.so模块实现的,所以本质一样 gevent更关注于io和其它 epool只是遇到io就切换,而gevent其它等待也切换

  5. python3.x 多路IO复用补充asyncio

    asyncio模块是python之父写的模块,按说应该是靠谱的,python3.6版本定义为稳定版本. 说明书:https://docs.python.org/3/library/asyncio.ht ...

  6. 基于select类型多路IO复用,实现简单socket并发

    还有很多缺限,如客户断开无限重复 以下转至老师博客: server: #!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = " ...

  7. Reactor模式,或者叫反应器模式 - 为什么用多路io复用提供吞吐量

    Reactor这个词译成汉语还真没有什么合适的,很多地方叫反应器模式,但更多好像就直接叫reactor模式了,其实我觉着叫应答者模式更好理解一些.通过了解,这个模式更像一个侍卫,一直在等待你的召唤,或 ...

  8. 20190925-03Redis端口号的由来及单线程加多路IO复用 000 024

  9. 应用层协议实现系列(一)——HTTPserver之仿nginx多进程和多路IO的实现

    近期在尝试自己写一个Httpserver,在粗略研究了nginx的代码之后,决定仿照nginx中的部分设计自己实现一个高并发的HTTPserver,在这里分享给大家. 眼下使用的较多的Httpserv ...

  10. IO复用,AIO,BIO,NIO,同步,异步,阻塞和非阻塞 区别参考

    参考https://www.cnblogs.com/aspirant/p/6877350.html?utm_source=itdadao&utm_medium=referral IO复用,AI ...

随机推荐

  1. POJ3414 Pots( BFS搜索)

    题目: 给你两个容器,分别能装下A升水和B升水,并且可以进行以下操作 FILL(i) 将第i个容器从水龙头里装满(1 ≤ i ≤ 2); DROP(i) 将第i个容器抽干 POUR(i,j) 将第i个 ...

  2. POJ 1015 Jury Compromise (完全背包)

    题目大意: 在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定.陪审团是由法官从公众中挑选的.先随机挑选n 个人作为陪审团的候选人,然后再从这n 个人中选m 人组成陪审团.选m 人的办法是:控方和辩 ...

  3. 【每日一题】8.Shortest Path (树上DFS)

    题目链接:Here 题意总结:给定的是无向图(树),要求把分成 \(n/2\) 对 让权值最小 思路: 看一下范围 在加上是一棵树 所以做法应该是dfs 复杂度为 \(\mathcal{O}(n)\) ...

  4. C#设计模式18——迭代器模式的写法

    是什么: 迭代器模式是一种行为型设计模式,它允许客户端通过一种统一的方式遍历集合对象中的元素,而无需暴露集合对象的内部结构. 为什么: 使用迭代器模式可以使得客户端程序与集合对象解耦,从而可以更加灵活 ...

  5. Cortex-M3 MCU的技术特点

    1.Cortex-M3 MCU的技术特点 MCU简单来说就是一个可编程的中央处理器(CPU)加上一些必要的外设.不管是中央处理器还是整个MCU都是复杂的时序数字电路,根据程序或者指令来完成特定的任务. ...

  6. 【TouchGFX】代码结构

    生成代码与用户代码 代码结构图示如下 据上图显示代码结构分为三层 引擎 这是TouchGFX提供的标准类,作为生成类的基类 生成 这是touchgfx designer生成的类,作为用户类的基类,这部 ...

  7. java - 对象装载数据返回

    1. 创建 Phone 类 package class_object; public class Phone { String brand; String color; double price; v ...

  8. 浏览器兼容 : IE10

    <script> /*@cc_on @*//*@ if (document.documentMode == 10) { // 只在 IE10 文档模式下运行,例如 IE10 浏览器或 IE ...

  9. [转帖]ElasticSearch 最全详细使用教程

    https://zhuanlan.zhihu.com/p/449555826?utm_source=weibo&utm_medium=social&utm_oi=27124941455 ...

  10. [转帖]【JVM】线程安全与锁优化

    线程安全 1.定义 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果 2. ...