1. 程式人生 > >Linux關於IO複用(poll模型)

Linux關於IO複用(poll模型)

POLL函式概念

Poll函式和select類似,但它是用檔案描述符而不是條件的型別來組織資訊的. 也就是說,一個檔案描述符的可能事件都儲存在struct pollfd中.與之相反,select用事件的型別來組織資訊,而且讀,寫和錯誤情況都有獨立的描述符掩碼.poll函式是POSIX:XSI擴充套件的一部分,它起源於UNIX System V

函式poll原型

包含標頭檔案<poll.h> 功能:與select函式功能相同 原型: int poll(struct pollfd *fdarray,unsigned long nfds,int timeout); 引數 fdarray是一個pollfd的機構體陣列用來表示表示檔案描述符的監視資訊 nfds引數給出了要監視的描述符數目 timeout引數是一個用豪秒錶示的時間,是poll在返回前沒有接收事件是應等待的時間,如果timeout的值為-1,poll就永遠不會超時.如果整數值為32個位元,那麼最大超時週期約為30分鐘 返回值:準備好描述字的個數,0-超時,1-出錯

Pollfd結構體

fd是檔案描述符值 event和revents是通過代表各種事件的標準符進行邏輯或運算構建而成的                  

Struct pollfd         {          

 int fd;            

short events;   //感興趣的事件          

 short revents;  //fd上觸發的事情     

    }

Poll函式事件標誌

 事件標誌符

    含義

POLLIN

無阻塞地讀除了具有高優先順序的資料之外的資料

POLLRONORM

無阻塞地讀常規資料

POLLRDBAND

無阻塞地讀具有優先順序的資料

POLLOUT

無阻塞的寫常規資料

Poll函式實現

與之前的select實現基本一致,實現c/s架構

首先伺服器:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/wait.h>
#include <sys/socket.h>  
#include <netinet/in.h> /* for struct sockaddr_in*/  
#include <sys/errno.h>  
#include <signal.h> 
#include <sys/select.h>
#include <poll.h>

//關於IO複用伺服器的poll

#define LISTEN_SIZE 1024
void error_exit(char *name)
{
   perror(name);
   exit(-1);
}
int main(int argc,char *argv[])
{
   int sockfd=socket(AF_INET,SOCK_STREAM,0);
   if(sockfd<0)
   {
        error_exit("create error");
   }
   //繫結地址(ip和埠號)
   struct sockaddr_in svraddr;
   memset(&svraddr,0,sizeof(svraddr));
   svraddr.sin_family=AF_INET;
   svraddr.sin_addr.s_addr=INADDR_ANY;
   //svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");第二種寫法
   svraddr.sin_port=htons(5555);
   int ret=bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr));
   if(ret<0)
   {
      error_exit("bind error");
   }
   //設定監聽引數back login 半連線數最大
   ret=listen(sockfd,1024);
   if(ret<0)
   {
      error_exit("listen error");
   }
   //建立poll結構體
   struct pollfd pollfds[LISTEN_SIZE]={0};
   int i=0;
   for(;i<LISTEN_SIZE;i++)
   {
       pollfds[i].fd=-1;
   }
   pollfds[0].fd=sockfd;
   pollfds[0].events=POLLIN;
   struct sockaddr_in removeaddr;
   int addr_len=sizeof(removeaddr);
   char *buf[1024]={0};
   while(1)
   {
      int nevent=poll(pollfds,LISTEN_SIZE,-1);
      if(nevent==0)
      {
            printf("timeout\n");
            continue;
      }
      else if(nevent<0)
      {
            error_exit("poll error");
      }
      if(pollfds[0].revents&POLLIN)
      {
         int fd=accept(sockfd,(struct sockaddr *)&removeaddr,&addr_len);
         if(fd<0)
         {
             error_exit("accept error");
          }
          for(i=1;i<LISTEN_SIZE;i++)
          {
                if(pollfds[i].fd==-1)
                {
                   pollfds[i].fd=fd;
                   pollfds[i].events=POLLIN;
                   break;
                }
          }
          for(i=1;i<LISTEN_SIZE;i++)
          {
                if(pollfds[i].fd==-1)
                {
                    continue;
                } 
                 //判斷是否為可讀事件
                if(pollfds[i].revents&POLLIN)
                {
                    int rdsize=read(pollfds[i].fd,buf,1024);
                    if(rdsize<=0)
                    {
                        printf("close %d\n",pollfds[i].fd);
                        close(pollfds[i].fd);
                        pollfds[i].fd=-1;
                    }
                    else
                    {
                        printf("read buf =%s,fd=%d\n",buf,pollfds[i].fd);
                    }
                }
          }
      }
   }
}

客戶端:

使用之前的程式碼,基本不變,貼上來

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/wait.h>
#include <sys/socket.h>  
#include <netinet/in.h> /* for struct sockaddr_in*/  
#include <sys/errno.h>  
#include <signal.h> 

//關於客戶端的socket
void error_exit(char *name)
{
   perror(name);
   exit(-1);
}
int main(int argc,char *argv[])
{
   if(argc<3)
   {
        printf("run program+ip+port\n");
        return-1;
   }
   int sockfd=socket(AF_INET,SOCK_STREAM,0);
   if(sockfd<0)
   {
        error_exit("create error");
   }
   //連線伺服器,設定伺服器的地址(ip和埠)
   struct sockaddr_in svraddr;
   memset(&svraddr,0,sizeof(svraddr));
   svraddr.sin_family=AF_INET;
   svraddr.sin_addr.s_addr= inet_addr(argv[1]);
   svraddr.sin_port=htons(atoi(argv[2]));
   int ret =connect(sockfd,(struct sockaddr *)&svraddr,sizeof(svraddr));
   if(ret<0)
   {
      error_exit("connect error");
   }

   write(sockfd,"hello",strlen("hello"));
   sleep(5); 
   close(sockfd);
   return 0;
}

結果:

客戶端執行後臺操作

伺服器接受到的結果

總結:相比較之前的select函式,poll沒有FD_SETSIZE這個限制,poll的出現可以解決select的fd數量限制