多路複用之用poll編寫服務器

poll使用一個結構體指針代替select中讀事件、寫事件及異常事件。

結構體組成:

wKiom1dH1SnDoHQAAAAd***1r9s405.png-wh_50

events(請求事件)關心該文件描述符的什麼事件

所關心的事件有:

wKiom1dH8V2yuEb5AABgpaMcaak827.png-wh_50


poll函數原型:int poll(struct pollfd* fds,nfds_t nfds,int timeout)

fds:爲一個struct pollfd結構體類型的數組,存放要檢測其狀態的socket描述符

nfds:表示結構體數組總大小

timeout:poll函數阻塞等待的時間

return val:

大於0:結構體數組中準備好讀、寫或出錯狀態的socket描述符的總數量;

等於0:結構體數組中沒有任何socket描述符就緒,poll超時,超時時間是timeout毫秒

小於: poll函數調用失敗

服務器端代碼:(針對單客戶端)

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<sys/socket.h>
  4 #include<sys/types.h>
  5 #include<netinet/in.h>
  6 #include<arpa/inet.h>
  7 #include<errno.h>
  8 #include<poll.h>
  9 #include<stdlib.h>
 10 #include<assert.h>
 11 
 12 #define MAX_SIZE 64
 13 #define BACK_LOG 5
 14 void Usage(const char* proc)
 15 {
 16     printf("Usage:%s [ip] [port]\n",proc);
 17 }
 18 
 19 static int startup(char* ip,int port)
 20 {
 21     assert(ip);
 22     int listen_sock=socket(AF_INET,SOCK_STREAM,0);
 23     if(listen_sock<0)
 24     {
 25         perror("socket");
 26         exit(1);
 27     }
 28     struct sockaddr_in local;
 29     local.sin_family=AF_INET;
 30     local.sin_port=htons(port);
 31     local.sin_addr.s_addr=inet_addr(ip);
 32     socklen_t size=sizeof(local);
 33     if(bind(listen_sock,(struct sockaddr*)&local,size)<0)
 34     {
 35         perror("bind");
 36         exit(2);
 37     }
 38     if(listen(listen_sock,BACK_LOG)<0)
 39     {
 40         perror("listen");
 41         exit(3);
 42     }
 43     return listen_sock;
 44 }
 45 int main(int argc,char* argv[])
 46 {
 47     if(argc!=3)
 48     {
 49         Usage(argv[0]);
 50         exit(1);
 51     }
 52     char *_ip=argv[1];
 53     int _port=atoi(argv[2]);
 54     int listen_sock=startup(_ip,_port);
 55     struct sockaddr_in client;
 56     socklen_t size=sizeof(client);
 57     struct pollfd fd_set[MAX_SIZE];
 58     fd_set[0].fd=listen_sock;
 59     fd_set[0].events=POLLIN;
 60     fd_set[0].revents=0;
 61     char buf[1024];
 62     int max_fd=0;
 63     int timeout=5000;
 64     int done=0;
 65     int i=1;
 66     int j=0;
 67     for(;i<MAX_SIZE;++i)
 68     {
 69         fd_set[i].fd=-1;
 70     }
 71     while(!done)
 72     {
 73         timeout=5000;
 74         switch(poll(fd_set,max_fd+1,timeout))
 75         {
 76             case -1:
 77                 perror("poll");
 78                 break;
 79             case 0:
 80                 printf("poll timeout...\n");
 81                 break;
 82             default:
 83             {
 84                 for(i=0;i<MAX_SIZE;++i)
 85                 {
 86                     if(fd_set[i].fd==listen_sock && \
 87                         (fd_set[i].revents&POLLIN))
 88                     {//**判斷是否爲監聽狀態且判斷它返回的哪一個就緒事件
 89                         int new_sock=accept(listen_sock,(struct sockaddr*)&client,&size);
 90                         if(new_sock<0)
 91                         {
 92                             perror("accept");
 93                             return -1;
 94                         }
 95                         printf("get a new conn... [fd:%d] [ip:%s]\n",\
 96                         new_sock,inet_ntoa(client.sin_addr));
 97                         for(j=0;i<MAX_SIZE;++j)
 98                         {
 99                             if(fd_set[j].fd==-1)
100                             {
101                                 fd_set[j].fd=new_sock;
102                                 fd_set[j].events=POLLIN;
103                                 break;
104                             }
105                         }
106                         if(j==MAX_SIZE)
107                         {
108                             printf("fd_set[] is full...\n ");
109                             close(new_sock);
110                             exit(1);
111                         }
112                         if(j>max_fd)
113                         {
114                             max_fd=j;
115                         }
116                
117                     }else if(fd_set[i].fd>0 &&\
118                      (fd_set[i].revents&POLLIN))
119                     { //表示讀事件是否已經就緒
120                         ssize_t _size=read(fd_set[i].fd,buf,sizeof(buf)-1);
121                         if(_size>0)
122                         {
123                             buf[_size]='\0';
124                             printf("client:%s",buf);
125                             fd_set[i]._events=POLLOUT;
126                             fd_set[i].revents=0;
127                         }
128                         else if(_size==0)
129                         {
130                             printf("client is closed...\n");
131                             close(fd_set[i].fd);
132                             fd_set[i].fd=-1;
133                         }
134                     }else if(fd_set[i].fd>0 &&\
135                          fd_set[i].revents&POLLOUT)
136                           {  //寫事件就緒
137                               int fd=fd_set[i].fd;
138                               write(fd,buf,sizeof(buf)-1);
139                               fd_set[i].events=POLLIN;
140                           }
141                 }
142             }
143         break;
144    
144         }
145     }
146 
147 
148     return 0;
149 }

客戶端代碼:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<sys/socket.h>
 4 #include<sys/types.h>
 5 #include<netinet/in.h>
 6 #include<arpa/inet.h> 
 7 #include<errno.h>
 8 #include<stdlib.h>
 9 #include<assert.h>
 10 
 11 void Usage(const char* proc)
 12 {
 13     printf("Usage:%s [remoteip] [remotrport]\n",proc);
 14 }
 15 
 16 int main(int argc,char* argv[])
 17 {
 18     if(argc!=3)
 19     {
 20         Usage(argv[0]);
 21         exit(1);
 22     }
 23     int sock=socket(AF_INET,SOCK_STREAM,0);
 24     if(sock<0)
 25     {
 26         perror("sock");
 27         exit(2);
 28     }
 29     char *_ip=argv[1];
 30     int _port=atoi(argv[2]);
 31     struct sockaddr_in remote;
 32     remote.sin_family=AF_INET;
 33     remote.sin_port=htons(_port);
 34     remote.sin_addr.s_addr=inet_addr(_ip);
 35     socklen_t len=sizeof(remote);
 36     int ret=connect(sock,(struct sockaddr*)&remote,len);
 37     if(ret<0)
 38     {
 39         perror("connect");
 40         return -1;
 41     }
 42     char buf[1024];
 43     while(1)
 44     {
 45         memset(buf,'\0',sizeof(buf));
 46         printf("please input:");
 47         fflush(stdout);
 48         if(read(0,buf,sizeof(buf)-1)>0)
 49         {
 50             write(sock,buf,strlen(buf));
 51         }
 52 
 53         ssize_t _size=read(sock,buf,sizeof(buf)-1);
 54         if(_size>0)
 55         {
 56             printf("server---client:%s",buf);
 57         }
 58     }
 59     close(sock);
 60     return 0;
 61 }

運行結果:

wKiom1dHAh2ye8NcAAB7cUsZVi8777.png-wh_50

總結:(select與poll之間的區別)

select:使用三個文件描述符集(分別爲讀事件、寫事件、異常事件)表示它們各自關心什麼事件,將對應的文件描述符設置進去

它存在一定的弊端:

1.每次輪詢前都要對所關心的文件描述符集進行設置及初始化(因爲它們是輸入輸出型參數),每次都要遍歷所有的文件描述符

2.它依賴文件描述符,但是文件描述符集的大小是有一定的限度的,當有大量的客戶端同時請求服務器時不適宜使用

3.它的三個文件描述符集當中若有多個被修改,每次修改都要從用戶態切換到內核態,內核態來負責修改文件描述符的狀態變化

poll:使用一個結構體數組來代替select中的三個文件描述符集,它的大小不受限制

優化:

1.若有需要關心的文件描述符時,將它保存到結構體中,並設置該描述符所關心的事件,每次輪詢完後若有無效的文件描述符時,將其描述符的狀態置-1,當再次調poll函數時會忽略它

2.當有新的文件描述符時,重新遍歷結構體,將出現第一個爲-1的狀態時,將它設置爲要關心的描述符事件狀態,每次有新的文件描述符加入時,更改要關心的文件描述符數量

3.調用poll函數後,結構體中的revents(返回已經就緒的事件)會存儲就緒事件狀態,重新調用poll之前,系統設置該狀態默認其爲0,重新監聽關心事件

但poll也有相應的弊端:當同一時刻有大量客戶端發來請求連接,但有隻有很少的事件處於就緒狀態,因此可能隨着監視描述符數量的增長,效率也會降低




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