I/O多路转接之poll 函数

http://blog.csdn.net/li_ning_/article/details/52167224

poll

一、poll()函数:

这个函数是某些Unix系统提供的用于执行与select()函数同等功能的函数,自认为poll和select大同小异,下面是这个函数的声明:

[cpp] view plain copy
  1. #include <poll.h>  
  2.   
  3. int poll(struct pollfd *fds, nfds_t nfds, int timeout);  

参数:

1.第一个参数:一个结构数组,struct pollfd:

        fds:是一个struct pollfd结构类型的数组,每个数组元素都是一个pollfd结构,用于指定测试某个给定描述字fd的条件。存放需要检测其状态的Socket描述符;每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况

结构如下:

[cpp] view plain copy
  1. struct pollfd{  
  2.  int fd;          //文件描述符  
  3.  short events;    //请求的事件  
  4.  short revents;   //返回的事件  
  5.  };  

  events和revents是通过对代表各种事件的标志进行逻辑或运算构建而成的。events包括要监视的事件(就是我需要关注的时间,是读?是写?还是出错?)poll用已经发生的事件填充revents。poll函数通过在revents中设置标志肌肤POLLHUP、POLLERR和POLLNVAL来反映相关条件的存在。不需要在events中对于这些标志符相关的比特位进行设置。如果fd小于0, 则events字段被忽略,而revents被置为0.标准中没有说明如何处理文件结束。文件结束可以通过revents的标识符POLLHUN或返回0字节的常规读操作来传达。即使POLLIN或POLLRDNORM指出还有数据要读,POLLHUP也可能会被设置。因此,应该在错误检验之前处理正常的读操作。

poll函数的事件标志符值:

常量说明
POLLIN普通或优先级带数据可读
POLLRDNORM普通数据可读
POLLRDBAND优先级带数据可读
POLLPRI高优先级数据可读
POLLOUT普通数据可写
POLLWRNORM普通数据可写
POLLWRBAND优先级带数据可写
POLLERR发生错误
POLLHUP发生挂起
POLLNVAL描述字不是一个打开的文件

  注意:

        1)后三个只能作为描述字的返回结果存储在revents中,而不能作为测试条件用于events中。

  2)第二个参数nfds:要监视的描述符的数目。

  3)最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。如果  它的值为-1,poll就永远都不会超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。 

timeout值说明
INFTIM永远等待
0立即返回,不阻塞进程
>0
等待指定数目的毫秒数


如果是对一个描述符上的多个事件感兴趣的话,可以把这些常量标记之间进行按位或运算就可以了;

比如:

对socket描述符fd上的读、写、异常事件感兴趣,就可以这样做:

[cpp] view plain copy
  1. struct pollfd  fds;  
  2.   
  3. fds[index].events=POLLIN | POLLOUT | POLLERR;  

当 poll()函数返回时,要判断所检测的socket描述符上发生的事件,可以这样做:

[cpp] view plain copy
  1. struct pollfd  fds;  
  2.   
  3. //检测可读TCP连接请求:  
  4. if((fds[nIndex].revents & POLLIN) == POLLIN)  
  5. {  
  6. <span style="white-space:pre">    </span>//接收数据,调用accept()接收连接请求  
  7. }  
  8.   
  9. //检测可写:  
  10. if((fds[nIndex].revents & POLLOUT) == POLLOUT)  
  11. {  
  12. <span style="white-space:pre">    </span>//发送数据  
  13. }  
  14.   
  15. //检测异常:  
  16. if((fds[nIndex].revents & POLLERR) == POLLERR)  
  17. {  
  18. <span style="white-space:pre">    </span>//异常处理  
  19. }  
二、实例TCP服务器的服务器程序

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <sys/socket.h>  
  5. #include <sys/types.h>  
  6. #include <netinet/in.h>  
  7. #include <netdb.h>   
  8. #include <string.h>  
  9. #include <errno.h>  
  10. #include <poll.h>   //for poll  
  11.   
  12. #define LISTENQ 1024  
  13. #define MAXLINE 1024  
  14. #define OPEN_MAX 50000  
  15.   
  16. #ifndef INFTIM   
  17. #define INFTIM -1   
  18. #endif               
  19.   
  20. int start_up(char* ip,int port)  //创建一个套接字,绑定,检测服务器  
  21. {  
  22.   //sock  
  23.   //1.创建套接字  
  24.   int sock=socket(AF_INET,SOCK_STREAM,0);     
  25.   if(sock<0)  
  26.   {  
  27.       perror("sock");  
  28.       exit(0);  
  29.   }  
  30.     
  31.   //2.填充本地 sockaddr_in 结构体(设置本地的IP地址和端口)  
  32.   struct sockaddr_in local;         
  33.   local.sin_port=htons(port);  
  34.   local.sin_family=AF_INET;  
  35.   local.sin_addr.s_addr=inet_addr(ip);  
  36.   
  37.   //3.bind()绑定  
  38.   if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)   
  39.   {  
  40.       perror("bind");  
  41.       exit(1);  
  42.   }  
  43.   //4.listen()监听 检测服务器  
  44.   if(listen(sock,back_log)<0)  
  45.   {  
  46.       perror("sock");  
  47.       exit(1);  
  48.   }  
  49.   return sock;    //这样的套接字返回  
  50. }  
  51.       
  52. int main(int argc, char *argv[])  
  53. {  
  54.   int i, maxi, connfd, sockfd;  
  55.   int nready;  
  56.   ssize_t n;  
  57.   socklen_t clilen;  
  58.   
  59.   struct sockaddr_in servaddr;  
  60.     socklen_t len=sizeof(servaddr);   
  61.   
  62.    char buf[BUFSIZ];  
  63.     struct pollfd client[OPEN_MAX]; // 用于poll函数第一个参数的数组,存放每次的文件描述符个数  
  64.   if( argc != 3 )  
  65.     {  
  66.        printf("Please input %s <hostname>\n", argv[0]);  
  67.      exit(2);  
  68.     }  
  69.   
  70.         int listenfd=start_up(argv[1],argv[2]);      //创建一个绑定了本地 ip 和端口号的套接字描述符  
  71.           
  72.       client[0].fd = listenfd;         //将数组中的第一个元素设置成监听描述字  
  73.       client[0].events = POLLIN;       //将测试条件设置成普通或优先级带数据可读(感兴趣的事件读、写、出错),此处书中为POLLRDNORM,*/  
  74.         client[0].revents = 0;           //真正发生的事件  
  75.   
  76.       for(i = 1;i < OPEN_MAX; ++i)     //数组中的其它元素将暂时设置成不可用  
  77.         {  
  78.             client[i].fd = -1;  
  79.         }  
  80.           
  81.       maxi = 0;  
  82.        while(1)  
  83.       {  
  84.          nready = poll(client, maxi+1,INFTIM);          //将进程阻塞在poll上  
  85.          if( client[0].revents & POLLIN)                //先测试监听描述字  
  86.          {  
  87.                 connfd = accept(listenfd,(struct sockaddr*)&servaddr, &clilen);  
  88.   
  89.               for(i = 1; i < OPEN_MAX; ++i)  
  90.                 {  
  91.                     if( client[i].fd < 0 )  
  92.                   {  
  93.                       client[i].fd = connfd;      //将新连接加入到测试数组中  
  94.                      client[i].events = POLLIN;  //POLLRDNORM; 测试条件普通数据可读  
  95.                       break;  
  96.                   }  
  97.                 if( i == OPEN_MAX )  
  98.                 {  
  99.                         printf("too many clients"); //连接的客户端太多了,都达到最大值了  
  100.                     exit(1);  
  101.                 }  
  102.   
  103.                 if( i > maxi )  
  104.                     maxi = i;        //maxi记录的是数组元素的个数  
  105.   
  106.                 if( --nready <= 0 )  
  107.                     continue;            //如果没有可读的描述符了,就重新监听连接  
  108.                 }  
  109.           }  
  110.   
  111.           for(i = 1; i <= maxi; i++)  //测试除监听描述字以后的其它连接描述字  
  112.           {  
  113.                 if( (sockfd = client[i].fd) < 0) //如果当前描述字不可用,就测试下一个  
  114.                   continue;  
  115.   
  116.               if(client[i].revents & (POLLIN | POLLERR))//如果当前描述字返回的是普通数据可读或出错条件  
  117.               {  
  118.                  if( (n = read(sockfd, buf, MAXLINE)) < 0) //从套接口中读数据  
  119.                  {  
  120.                       if( errno == ECONNRESET) //如果连接断开,就关闭连接,并设当前描述符不可用  
  121.                       {  
  122.                          close(sockfd);  
  123.                          client[i].fd = -1;  
  124.                       }  
  125.                       else  
  126.                           perror("read error");  
  127.                   }  
  128.                 else if(n == 0) //如果数据读取完毕,关闭连接,设置当前描述符不可用  
  129.                   {  
  130.                       close(sockfd);  
  131.                       client[i].fd = -1;  
  132.                   }  
  133.                   else  
  134.                       write(sockfd, buf, n); //打印数据  
  135.                if(--nready <= 0)  
  136.                    break;  
  137.               }  
  138.           }  
  139.      }  
  140.      exit(0);  
  141.  }  
赐教!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384098.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

链表相关笔试面试题

1.判断两个链表是否相交 两个链表是否相交可分为以下几种情况     &#xff08;1&#xff09;两个链表都不带环&#xff0c;此时两个链表所对应的最后一个节点是相等的     &#xff08;2&#xff09;两个链表一个带环&#xff0c;一个不带环&#xff0c;两个链表一定…

Linux经典问题—五哲学家就餐问题

http://m.blog.csdn.net/aspenstars/article/details/70149038 一、问题介绍 由Dijkstra提出并解决的哲学家进餐问题(The Dinning Philosophers Problem)是典型的同步问题。该问题是描述有五个哲学家共用一张圆桌&#xff0c;分别坐在周围的五张椅子上&#xff0c;在圆桌上有五…

修改之前的myshell使之支持输入输出重定向

1.open函数     这个函数是打开一个文件&#xff08;文件名叫pathname),以 flag 权限打开&#xff0c;flag 包括了以下几种 O_RDONLY&#xff08;只读&#xff09;, O_WRONLY&#xff08;只写&#xff09;, O_RDWR&#xff08;读写&#xff09;&#xff0c;当文件打开成…

链表相关的算法题大汇总 — 数据结构之链表奇思妙想

http://blog.csdn.net/lanxuezaipiao/article/details/22100021基本函数&#xff08;具体代码实现见后面&#xff09; 1&#xff0c;构造节点 //定义节点类型 struct Node { int value; Node*next; }; 2&#xff0c;分配节点 //之所以要分配节点原因是需要在分配函数中…

匿名管道

1.进程通信的目的 (1) 数据传输: 一个进程需要将它的数据传输给另一个进程     (2) 资源共享: 多个进程之间共享同样的资源     (3) 通知事件: 一个进程需要向另一个或一组进程发送消息, 通知它们发生了什么事情 2.管道 管道是一种进程之间通信的一种方式, 我们把从…

将信号量代码生成静态库以及动态库

1.信号量相关代码生成静态库 2.信号量相关代码生成动态库

C++11 标准新特性:Defaulted 和 Deleted 函数

https://www.ibm.com/developerworks/cn/aix/library/1212_lufang_c11new/index.html Defaulted 函数 背景问题 C 的类有四类特殊成员函数&#xff0c;它们分别是&#xff1a;默认构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。这些类的特殊成员函数负责创建、初始化、…

顺序表实现栈相关操作

1.栈的相关概念 栈是一种特殊的线性表, 其中只允许在固定的一端进行插入和删除元素.进行数据插入和删除的一端叫做栈顶, 另一端成为栈底. 不含任何元素的栈称为空栈, 栈又称为先进先出的线性表. 2. 顺序栈的结构 3. 顺序栈的具体操作 (1). 数据结构 typedef char SeqStackTyp…

双向带环带头结点的链表实现栈

1. 数据结构 利用带头结点带环的结点实现栈的相关操作.因此, 每一个结点包括了一个前驱, 一个后继, 还有一个数据成员 typedef char DLinkStackType;typedef struct DLinkStack {DLinkStackType data;struct DLinkStack* next;struct DLinkStack* prev; }DLinkStack;2. 初始化…

C++11 标准新特性:委派构造函数

https://www.ibm.com/developerworks/cn/rational/1508_chenjing_c11/index.html陈 晶2015 年 8 月 11 日发布WeiboGoogle用电子邮件发送本页面 1本文首先介绍了在委派构造函数提出之前类成员构造所面临的问题&#xff0c;再结合实例介绍了委派构造函数的用法&#xff0c;并说明…

顺序表实现队列

一. 队列相关概念 队列是只允许在一段进行插入元素, 在另一端进行删除元素的线性表,即只允许对队列进行尾插,头删的操作.队列具有先进先出, 后进后出的特性.          1.初始化 void SeqQueInit(SeqQue* q) {if(q NULL){return;//非法输入}q -> head 0;q -> …

链表实现队列

上篇博客是用顺序表实现队列, 现在用双向带头结点带环链表实现对队列的出队列, 入队列, 取队首元素, 以及销毁队列的相关操作 1.初始化链表 void DLinkQueInit(DLinkQue** q) {if(q NULL){return;//非法输入}if(*q NULL){return;//非法输入带头结点的链表至少有一个傀儡结点…

HDU - 1796——容斥原理+二进制枚举

【题目描述】 Now you get a number N, and a M-integers set, you should find out how many integers which are small than N, that they can divided exactly by any integers in the set. For example, N12, and M-integer set is {2,3}, so there is another set {2,3,4,…

信号的基本概念以及信号的产生

一. 信号产生的场景 1. 用户输入命令, 在shell 启动一个前台进程      2. 当用户按一下 Ctrl C 的时候,从键盘产生一个硬件中断      3. 此时CPU 正在执行这个进程的带代码, 则该进程的执行代码暂停执行, CPU 从用户态切换到内核态处理该硬件中断.      4. 中断…

信号的阻塞

一. 阻塞信号 1.信号的相关概念     (1) 递达: 实际执行信号的处理动作称为信号的递达     (2) 未决: 信号从产生到递达之间的过程叫做信号的未决     (3) 阻塞: 进程可以选择阻塞某个信号, 被阻塞的信号产生时将保持在未决状态, 直到进程解除该信号的屏蔽, 才…

信号的捕捉以及SIGCHLD信号

一. 信号的捕捉定义 用户提供一个处理函数, 要求内核在处理信号时必须切换到用户态,执行这个函数, 这种方式就叫做信号的捕捉 二. 图解信号的捕捉过程 1. 由上图可以看出,当处理信号的执行动作时用户自定义的时候,此时就需返回该函数去调用该函数, 这就叫做信号的捕捉. 以前我…

最小栈的实现

所谓最小栈, 就是当前栈顶元素最小, 我们可以这样做, 每次在入栈之前, 将待入栈元素与栈顶元素相比, 每次现将待入栈的元素先入栈, 再将带入栈的元素和较小的元素入栈, 这样就可以保证每次栈顶元素是最小元素. 在出栈的时候规定每次出栈两个元素,这样就可以保证在出栈之后栈顶元…

用C++实现单链表的创建、逆置和输出 的两种方法

http://blog.csdn.net/lfeng_coding/article/details/47300563 题目描述&#xff1a;在已知单链表头节点的情况下&#xff0c;设计算法逆置单链表并输出 方法一&#xff1a;采用首先将头节点指向空&#xff0c;让其变为尾节点&#xff0c;然后利用中间节点 p、q 将其后的节点一…

两个栈实现一个队列

利用两个栈实现一个队列思路是这样的. 首先这个队列包含两个栈, 然后一个栈用来入队列, 一个栈用来出队列 typedef struct QueBy2Stack {SeqStack input;SeqStack output; }QueBy2Stack; 1. 初始化 void QueBy2StackInit(QueBy2Stack* stack) {if(stack NULL){return;//非法…

Linux--线程死锁

http://blog.csdn.net/gebushuaidanhenhuai/article/details/73799824 线程为什会死锁&#xff1f;&#xff1f;“锁”又是什么东西&#xff1f;我们这篇博客主要讲一下为什么要给线程加锁&#xff0c;为什么会出现线程死锁&#xff0c;线程死锁怎么解决。 互斥锁 在我的上篇博…