poll

一、poll()函数:

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

#include <poll.h>

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描述符的情况

结构如下:

 struct pollfd{
  int fd; //文件描述符
  short events; //请求的事件
  short revents; //返回的事件
  };

  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上的读、写、异常事件感兴趣,就可以这样做:

struct pollfd  fds;

fds[index].events=POLLIN | POLLOUT | POLLERR;

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

struct pollfd  fds;

//检测可读TCP连接请求:
if((fds[nIndex].revents & POLLIN) == POLLIN)
{
<span style="white-space:pre"> </span>//接收数据,调用accept()接收连接请求
} //检测可写:
if((fds[nIndex].revents & POLLOUT) == POLLOUT)
{
<span style="white-space:pre"> </span>//发送数据
} //检测异常:
if((fds[nIndex].revents & POLLERR) == POLLERR)
{
<span style="white-space:pre"> </span>//异常处理
}

二、实例TCP服务器的服务器程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h> 
#include <string.h>
#include <errno.h>
#include <poll.h> //for poll #define LISTENQ 1024
#define MAXLINE 1024
#define OPEN_MAX 50000 #ifndef INFTIM
#define INFTIM -1
#endif int start_up(char* ip,int port) //创建一个套接字,绑定,检测服务器
{
//sock
//1.创建套接字
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("sock");
exit(0);
} //2.填充本地 sockaddr_in 结构体(设置本地的IP地址和端口)
struct sockaddr_in local;
local.sin_port=htons(port);
local.sin_family=AF_INET;
local.sin_addr.s_addr=inet_addr(ip); //3.bind()绑定
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
exit(1);
}
//4.listen()监听 检测服务器
if(listen(sock,back_log)<0)
{
perror("sock");
exit(1);
}
return sock; //这样的套接字返回
} int main(int argc, char *argv[])
{
  int i, maxi, connfd, sockfd;
  int nready;
  ssize_t n;
  socklen_t clilen;   struct sockaddr_in servaddr;
socklen_t len=sizeof(servaddr);   char buf[BUFSIZ];
struct pollfd client[OPEN_MAX]; // 用于poll函数第一个参数的数组,存放每次的文件描述符个数
  if( argc != 3 )
{
   printf("Please input %s <hostname>\n", argv[0]);
exit(2);
} int listenfd=start_up(argv[1],argv[2]); //创建一个绑定了本地 ip 和端口号的套接字描述符    client[0].fd = listenfd; //将数组中的第一个元素设置成监听描述字
   client[0].events = POLLIN; //将测试条件设置成普通或优先级带数据可读(感兴趣的事件读、写、出错),此处书中为POLLRDNORM,*/
client[0].revents = 0; //真正发生的事件    for(i = 1;i < OPEN_MAX; ++i) //数组中的其它元素将暂时设置成不可用
{
client[i].fd = -1;
}    maxi = 0;
  while(1)
   {
   nready = poll(client, maxi+1,INFTIM); //将进程阻塞在poll上
   if( client[0].revents & POLLIN) //先测试监听描述字
   {
connfd = accept(listenfd,(struct sockaddr*)&servaddr, &clilen);    for(i = 1; i < OPEN_MAX; ++i)
{
if( client[i].fd < 0 )
   {
  client[i].fd = connfd; //将新连接加入到测试数组中
   client[i].events = POLLIN; //POLLRDNORM; 测试条件普通数据可读
  break;
   }
   if( i == OPEN_MAX )
  {
   printf("too many clients"); //连接的客户端太多了,都达到最大值了
  exit(1);
   }    if( i > maxi )
   maxi = i; //maxi记录的是数组元素的个数    if( --nready <= 0 )
   continue; //如果没有可读的描述符了,就重新监听连接
}
   }    for(i = 1; i <= maxi; i++) //测试除监听描述字以后的其它连接描述字
   {
if( (sockfd = client[i].fd) < 0) //如果当前描述字不可用,就测试下一个
   continue;    if(client[i].revents & (POLLIN | POLLERR))//如果当前描述字返回的是普通数据可读或出错条件
   {
  if( (n = read(sockfd, buf, MAXLINE)) < 0) //从套接口中读数据
   {
   if( errno == ECONNRESET) //如果连接断开,就关闭连接,并设当前描述符不可用
   {
   close(sockfd);
   client[i].fd = -1;
   }
   else
   perror("read error");
   }
  else if(n == 0) //如果数据读取完毕,关闭连接,设置当前描述符不可用
   {
   close(sockfd);
   client[i].fd = -1;
   }
   else
   write(sockfd, buf, n); //打印数据
   if(--nready <= 0)
   break;
   }
   }
   }
   exit(0);
 }

赐教!

I/O多路转接之poll 函数的更多相关文章

  1. 多路转接之poll和select

    先看poll(): #include <stdio.h> #include <stdlib.h> #include <string.h> #include < ...

  2. Linux下I/O多路转接之epoll(绝对经典)

    epoll 关于Linux下I/O多路转接之epoll函数,什么返回值,什么参数,我不想再多的解释,您不想移驾,我给你移来: http://blog.csdn.net/colder2008/artic ...

  3. IO多路转接select和poll

    select IO多路复用的设置方法与信号的屏蔽有点相似: 信号屏蔽需要先设定一个信号集, 初始化信号集, 添加需要屏蔽的信号, 然后用sigprocmask设置 IO多路转接需要先设定一个文件描述符 ...

  4. UNIX环境高级编程——I/O多路转接(select、pselect和poll)

    I/O多路转接:先构造一张有关描述符的列表,然后调用一个函数,直到这些描述符中的一个已准备好进行I/O时,该函数才返回.在返回时,它告诉进程哪些描述符已准备好可以进行I/O. poll.pselect ...

  5. select函数与I/O多路转接

    select函数与I/O多路转接 相作大家都写过读写IO操作的代码,例如从socket中读取数据可以使用如下的代码: while( (n = read(socketfd, buf, BUFSIZE) ...

  6. 【Nginx】I/O多路转接之select、poll、epoll

    当需要读两个以上的I/O的时候,如果使用阻塞式的I/O,那么可能长时间的阻塞在一个描述符上面,另外的描述符虽然有数据但是不能读出来,这样实时性不能满足要求,大概的解决方案有以下几种: 1.使用多进程或 ...

  7. select与poll函数介绍

    select与poll函数介绍 在所有依从POSIX的平台上,select函数使我们可以执行I/O多路转接.传向select的参数告诉内核: 1)我们所关心的描述符 2)对于每个描述符我们所关心的状态 ...

  8. 高级I/O之I/O多路转接——pool、select

    当从一个描述符读,然后又写到另一个描述符时,可以在下列形式的循环中使用阻塞I/O: ) if (write(STDOUT_FILENO, buf, n) != n) err_sys("wri ...

  9. I/O多路转接 --- UNIX环境高级编程

    I/O多路转接技术:先构造一张有关描述符的列表,然后调用一个函数,知道这些描述符中的一个已准备好进行I/O时,给函数才返回.在返回时,它告诉进程哪些描述符已准备好可以进行I/O. poll.selec ...

随机推荐

  1. 自定义JS乘法运算误差解决!

    在实际开发中遇到这样一个乘法公式:数量*单价=总价 像这样的浮点数列子:200*8.2,JS算出的结果是: 像这种浮点数的乘法计算就会有误差,我们需要得到准确的值应该是:1640,与我们后台C#计算结 ...

  2. js实现内容点击复制

    <!DOCTYPE html><html> <head> <script type="text/javascript"> funct ...

  3. 洛谷P3402 【模板】可持久化并查集(可持久化线段树,线段树)

    orz TPLY 巨佬,题解讲的挺好的. 这里重点梳理一下思路,做一个小小的补充吧. 写可持久化线段树,叶子节点维护每个位置的fa,利用每次只更新一个节点的特性,每次插入\(logN\)个节点,这一部 ...

  4. 【CJOJ1167】【洛谷1894】[USACO4.2]完美的牛栏

    题面 Description 农夫约翰上个星期刚刚建好了他的新牛棚,他使用了最新的挤奶技术.不幸的是,由于工程问题,每个牛栏都不一样.第一个星期,农夫约翰随便地让奶牛们进入牛栏,但是问题很快地显露出来 ...

  5. [BZOJ1016] [JSOI2008] 最小生成树计数 (Kruskal)

    Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的 ...

  6. 什么是Docker??

    ​​Docker是一个轻量级虚拟机,也是一种Linux容器,它突破了以往的沙盒技术,解放了应用部署,让PaaS的应用场景更为广泛. ​ docker是通过内核虚拟化技术((namespaces及cgr ...

  7. 【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  8. LINUX下解决TIME_WAIT等网络问题

    修改配置文件 /etc/sysctl.conf net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_tw_recycle = 1net.ipv4.tcp_fin_timeout ...

  9. python打造社工脚本

    0x00前言: 大家都知道图片是有Exif信息的.里面包含着 你的GPS信息.还有拍摄时间等等的敏感信息. 0x01准备: exifread requests 0x02思路: 读取图片的Exif信息. ...

  10. android 获取wifi列表,如果你忽略了这个细节,可能你的软件会崩溃

    一:业务描述 最近公司有一个小需求,用户点击wifi扫描按钮(注意:是用户主动点击wifi扫描按钮),app去扫描附近的wifi,显示在listView中,仅此而已,app都不用去连接某个wifi,看 ...