多路I/O poll編寫服務器

一.poll (多路複用I/O poll)

 和select()函數一樣,poll函數也可以執行多路I/O複用,但poll與select相比,沒有像select那樣構建結構體的三個數組(針對每一個條件分別有一個數組:讀事件,寫事件,異常),然後檢查從0到nfds每個文件描述符。poll採用了一個單獨的結構體pollfd數組,由fds指針指向這個組。pollfd結構體定義如下:

#include <sys/poll.h>

struct pollfd {
int fd; //文件描述符
short events; //當下文件描述符關心的事件
short revents; //文件描述符關心的事件就緒
};

每一個pollfd結構體指定了一個被監視的文件描述符,可以傳遞多個結構體,指示poll()監視多個文件描述符。每個結構體的events域是監視該文件描述符關心的事件,由用戶來設置這個域。revents域是文件描述符的關心事件發生了。內核在調用返回時設置這個域。events域中請求的任何事件都可能在revents域中返回

函數原型:int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);

參數依次爲: 結構體數組、有效文件描述符個數、超時時間


1>示例1使用poll監控輸入輸出

  程序代碼:

  1 #include<stdio.h>
  2 #include<errno.h>
  3 #include<poll.h>
  4 #include<string.h>
  5 int main()
  6 {
  7   struct pollfd fd_set[1];//定義一個結構體數組
  8   fd_set[0].fd=0;
  9   fd_set[0].events=POLLIN;//可讀
 10   fd_set[0].revents=0;
 11   int timeout=5000;
 12    while(1)
 13    {
 14      switch(poll(fd_set,1,timeout))
 15      {
 16        case -1://chucuo
 17        perror("poll");
 18        break;
 19         case 0://timeout
 20         printf("poll timeout\n");
 21         break;
 22        default: //normal
 23        {
24           if((fd_set[0].fd==0)&&((fd_set[0].revents)&POLLIN))
 25              {
 26                //DATA IS READY
 27                char buf[1024];
 28                memset(buf,'\0',sizeof(buf));
 29                ssize_tsize=read(0,buf,sizeof(buf)-1);
 30                if(size>0)
 31                   {
 32                     buf[size]='\0';
 33                    printf("echo->  %s",buf);
 34
 35                    }
 36                     else if(size==0)
 37                     {
 38                        printf("closing.........\n");
 39                      }
 40
 41              }
 42           }
 43           break;
 44          }
 45        }
 46     return 0;

程序結果:

wKioL1dJROKhj9nQAAAbvGzqEhM979.png-wh_50

2>示例2搭建poll服務器

程序代碼:

 // poll_server.c
  
  1 #include<stdio.h>
  2 #include<errno.h>
  3 #include<poll.h>
  4 #include<string.h>
  5 #include<sys/types.h>
  6 #include<arpa/inet.h>
  7 #include<netinet/in.h>
  8 #include<sys/socket.h>
  9 #include<stdlib.h>
 10 
 11 static void usage(char *proc)
 12 {
 13    printf("usage: %s [ip] [port] ",proc);
 14 }
 15 
 16 static int start(int port,char *ip)
 17 {
 18     int sock=socket(AF_INET,SOCK_STREAM,0);
 19     if(sock<0)
 20     {
 21       perror("socket");
 22        exit(1);
 23     }
 24     struct sockaddr_in local;
 25     local.sin_family=AF_INET;
 26     local.sin_port=htons(port);
 27     local.sin_addr.s_addr=inet_addr(ip);
 28     if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
 29      {
 30       perror("bind");
 31       exit(2);
 32      }
 33     if(listen(sock,5)<0)
 34      {
 35        perror("listen");
 36        exit(3);
 37      }
 38      return sock;
 39 }
 40 int main(int argc,char*argv[])
 41 {
 42   if(argc!=3)
 43   {
 44      usage(argv[0]);
 45      return 1;
 46   }
 47   int port=atoi(argv[2]);
 48   char* ip=argv[1];
 49   int listen_sock=start(port,ip);
 50   struct pollfd fd_set[2];
 51   fd_set[0].fd=listen_sock;
 52   fd_set[0].events=POLLIN;
 53   fd_set[0].revents=0;
 54   int timeout=8000;
 55   int fd_setn=sizeof(fd_set)/sizeof(fd_set[0]);
 56   struct sockaddr_in client;
 57   socklen_t len=sizeof(client);
 58   int i=1;
 59   for(;i<fd_setn;++i)
 60    {
 61      fd_set[i].fd=-1;
 62    }
 63 
 64   int max_fd=0;
 65 while(1)
 66  {
 67 
 68      switch(poll(fd_set,max_fd+1,timeout))
 69      {
 70        case -1://chucuo
 71        perror("poll");
 72        break;
 73         case 0://timeout
 74         printf("poll timeout\n");
 75         break;
 76        default: //normal
 77        {
 78           for(i=0;i<fd_setn;++i)
 79           {//listen_sock時,接收連接請求
 80            if((fd_set[i].fd==listen_sock)&&((fd_set[i].revents)&POLLIN))
 81              {
 82             intnew_sock=accept(listen_sock,(struct sockaddr*)&client,&len);
 83                if(new_sock<0)
 84                 {
 85                   perror("accept");
 86                   continue;
 87                 }
 88                printf("get a connect......\n");
 89                 int j=0;
 90                for(j=0;j<fd_setn;++j)
 91                  {
 92                   if(fd_set[j].fd==-1)
 93                      {   //將new_sock添加進去
 
 94                         fd_set[j].fd=new_sock;
 95                         fd_set[j].events=POLLIN;
 96                         fd_set[j].revents=0;
 97                         break;
 98                      }
 99                  }
100 
101                if(j==fd_setn)
102                 {
103                    close(new_sock);
104 
105             }
106                 if(j>max_fd)//有效文件描述符個數
107                   {
108                     max_fd=j;
109                    }
110                }
111                                 //可讀數據
112                else if((fd_set[i].fd>0)&&((fd_set[i].revents)&POLLIN))
113                {
114 
115                   char buf[1024];
116                   ssize_t s=read(fd_set[i].fd,buf,sizeof(buf)-1);
117                   if(s>0)//讀成功
118                    {
119                     buf[s]='\0';
120                     printf("client#:  %s\n",buf);
121                     write(fd_set[i].fd,buf,strlen(buf));
122 
123                     }
124                    else if(s==0)
125                     {
126                       close(fd_set[i].fd);
127                       int xj=1;
                                                 //將其置爲無效狀態

128                       for(;xj<fd_setn;++i)
129                        {
130                           if(fd_set[xj].fd!=-1&&(xj!=i))
131                            {
132                             int tmp=fd_set[i].fd;
133                             fd_set[i].fd=fd_set[xj].fd;
134                             fd_set[xj].fd=tmp;
135                              }
136                           }
137                      }
138                    }
139 
140                 }
141 
142            }
143           break;
144          }
145 
146        }
147     return 0;
148 }
//poll_client.c
  1 #include<stdio.h>
  2 #include<errno.h>
  3 #include<poll.h>
  4 #include<string.h>
  5 #include<sys/types.h>
  6 #include<arpa/inet.h>
  7 #include<netinet/in.h>
  8 #include<sys/socket.h>
  9 #include<stdlib.h>
 10 
 11 static void usage(char *proc)
 12 {
 13    printf("usage: %s [ip] [port] ",proc);
 14 }
 15 
 16 
 17 int main(int argc, char*argv[])
 18 {
 19   if(argc!=3)
 20   {
 21      usage(argv[0]);
 22      return 1;
 23   }
 24   int port=atoi(argv[2]);
 25   char* ip=argv[1];
 26   int sock=socket(AF_INET,SOCK_STREAM,0);
 27   if(sock<0)
 28   {
 29       perror("socket");
 30       exit(1);
 31   }
 32    struct sockaddr_in remote;
 33    remote.sin_family=AF_INET;
 34    remote.sin_port=htons(port);
 35    remote.sin_addr.s_addr=inet_addr(ip);
 36       int ret=connect(sock,(struct sockaddr*)&remote,sizeof(remote));//請求連接
 37     if(ret<0)
 38   {
 39      perror("coonect");
 40      exit(3);
 41 
 42    }
 43    char buf[1024];
 44    while(1)
 45    {
 46      memset(buf,'\0',sizeof(buf));
 47      printf("please enter: ");
  48      fflush(stdout);
  49      ssize_t s=read(0,buf,sizeof(buf)-1);
  50      if(s>0)
  51       {
  52        write(sock,buf,strlen(buf));
  53        }
        //回顯消息
  54          ssize_t size=read(sock,buf,sizeof(buf)-1);
    55     {
    56       printf("server->client :%s\n",buf);
    57 
    58     }
    59    }
  56       printf("server->client :%s\n",buf);
  59    }
  60     close(sock);
  61     return 0;
  62 }

 程序結果:

wKioL1dJSPmg7AlTAAAdSDRiyhk598.png-wh_50

wKiom1dJSAGieparAAAngsHN8Cw065.png-wh_50

poll總結:

1.poll與select一樣實現需要自己不斷輪詢所有fd集合,直到設備就緒,期間可能要睡眠和喚醒多次交替。

2.poll與select一樣每次調用都要把fd集合從用戶態往內核態拷貝一次。

3.select,poll都是IO多路複用的機制。I/O多路複用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。但select,poll本質上都是同步I/O,因爲他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的。





 

 






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章