zoukankan      html  css  js  c++  java
  • I/O多路转接之poll 函数

    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>
    ", 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);
     }
    赐教!

  • 相关阅读:
    Mybatis 原始dao CRUD方法
    JQuery的焦点事件focus() 与按键事件keydown() 及js判断当前页面是否为顶级页面 子页面刷新将顶级页面刷新 window.top.location
    使用actionerror做失败登录验证
    Java项目中的下载 与 上传
    shiro框架 4种授权方式 说明
    javascript 中数组的创建 添加 与将数组转换成字符串 页面三种提交请求的方式
    序列化表单为json对象,datagrid带额外参提交一次查询 后台用Spring data JPA 实现带条件的分页查询 多表关联查询
    Spring data JPA 理解(默认查询 自定义查询 分页查询)及no session 三种处理方法
    orcal 数据库 maven架构 ssh框架 的全注解环境模版 maven中央仓库批量删除lastupdated文件后依然是lastupdated解决方法 mirror aliyun中央仓库
    EasyUI加zTree使用解析 easyui修改操作的表单回显方法 验证框提交表单前验证 datagrid的load方法
  • 原文地址:https://www.cnblogs.com/melons/p/5791789.html
Copyright © 2011-2022 走看看