I/O多路轉接之poll——基於TCP協議

1. 函數

wKiom1dzJNCg5hGZAAAXxJzCuJo233.png


a. 參數:

(1)fds:是一個struct pollfd結構類型的指針,指向用於存放需要檢測狀態的Socket描述符;

每當調用這個函數之後,系統不會清空這個數組,操作起來比較方便;特別是對於socket連接比較多的情況下,在一定程度上可以提高處理的效率;這一點與select()函數不同,調用select()函數之後,select()函數會清空它所檢測的socket描述符集合,導致每次調用select()之前都必須把socket描述符重新加入到待檢測的集合中;因此,select()函數適合於只檢測一個socket描述符的情況,而poll()函數適合於大量socket描述符的情況;

wKiom1d0a2ywCYtFAAAS83He4Qs347.png

  1. fd:表示所要關心的文件描述符;

  2. events:表示該文件描述符所關心的事件,這是一個輸入型參數,要告訴操作系統這個文件描述符對應的事件所關心的操作事件是什麼,比如讀或寫;

  3. revents:表示當poll返回時告訴用戶什麼操作事件是就緒的,比如如果POLLIN是就緒的,那麼返回時revent的值就是POLLIN,告訴用戶fd事件的POLLIN是就緒的;是一個輸出型參數。


(2)nfds:標記指針fds所指向的結構體元素的總數量;


(3)timeout:poll函數調用阻塞的時間,單位:毫秒。(和select的一樣)

如果參數timeout設爲:

  1. INFTIM:select將一直被阻塞,直到某個文件描述符上發生了事件。

  2. 0:select將以非阻塞方式等,即檢測描述符集的狀態後立即返回,並不等待外部事件的發生。

  3. 大於0的值:poll()函數會阻塞timeout所指定的毫秒時間長度之後返回,如果在指定的時間段裏沒有事件發生,select將超時返回0。


c. 返回值
(1)成功:則返回fds指針所指向的內容中準備好的讀、寫或出錯狀態的那些socket描述符的總數量(返回值>0);
(2)poll函數調用失敗:返回-1,同時會自動設置全局變量errno;;
(3)超過timeout時間,返回0。(沒有任何socket描述符準備好讀、寫,或出錯)


2. poll和select對比

(1)不同:

  1. select使用三個位圖來表示三個fdset的方式,poll使用一個 pollfd的指針實現。

  2. pollfd結構包含了要監視的event和發生的event,不再使用select“參數-值”傳遞的方式。

  3. pollfd並沒有最大數量限制(但是數量過大後性能也是會下降)。 

(2)相同:

  1. 和select函數一樣,poll返回後,需要輪詢pollfd來獲取就緒的描述符。

  2. 從上面看,select和poll都需要在返回後,通過遍歷文件描述符來獲取已經就緒的socket。事實上,同時連接的大量客戶端在一時刻可能只有很少的處於就緒狀態,因此隨着監視的描述符數量的增長,其效率也會線性下降。


3.代碼實現

//poll_server.c
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<assert.h>
  4 #include<poll.h>
  5 #include<unistd.h>
  6 #include<sys/socket.h>
  7 #include<sys/types.h>
  8 #include<arpa/inet.h>
  9 #include<netinet/in.h>
 10 
 11 #define _BACKLOG_ 5
 12 #define _NUM_ 10
 13 
 14 static void usage(const char* proc)
 15 {
 16     printf("usage:%s [ip]  [port]\n",proc);
 17 }
 18 static int startup(char* ip,int port)
 19 {
 20     assert(ip);
 21     //
 22     int sock=socket(AF_INET,SOCK_STREAM,0);
 23     if(sock<0)
 24     {
 25         perror("socket");
 26         exit(1);
 27     }
 28     //
 29     struct sockaddr_in local;
 30     local.sin_family=AF_INET;
 31     local.sin_port=htons(port);
 32     local.sin_addr.s_addr=inet_addr(ip);
 33     //
 34     if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
 35     {
 36         perror("bind");
 37         exit(2);
 38     }
 39     if(listen(sock,_BACKLOG_)<0)
 40     {
 41         perror("listen");
 42         exit(3);
 43     }
 44     return sock;
 45 }
 46 int main(int argc,char* argv[])
 47 {
 48     if(argc!=3)
 49     {
 50         usage(argv[0]);
 51         exit(1);
 52     }
 53     char* ip=argv[1];
 54     int port=atoi(argv[2]);
 55 
 56     int listen_sock=startup(ip,port);
 57 
 58     struct sockaddr_in client;
 59     socklen_t len=sizeof(client);
 60 
 61     struct pollfd fds[_NUM_];
 62     int max_fd=1;//int max_fd=fds[0].fd
 63     int _timeout=5000;
 64     fds[0].fd=listen_sock;//why locate
 65     fds[0].events=POLLIN;
 66     fds[0].revents=0;
 67 
 68     size_t i=1;
 69     for(;i<_NUM_;i++)
 70     {
 71         fds[i].fd=-1;
 72         fds[i].events=0;
 73         fds[i].revents=0;
 74     }
 75     while(1)
 76     {
 77         switch(poll(fds,max_fd,_timeout))
 78         {
 79             case -1:
 80                 perror("poll");
 81                 break;
 82             case 0:
 83                 printf("timeout...\n");
 84                 break;
 85             default:
 86                 {
 87                     i=0;
 88                     for(;i<_NUM_;++i)
 89                     {
 90                         if((fds[i].fd==listen_sock)&&(fds[i].revents==POLLIN    ))
 91                         {
 92                             int new_sock=accept(listen_sock,(struct sockaddr    *)&client,&len);
 93                             if(new_sock<0)
 94                             {
 95                                 perror("accept");
 96                                 continue;
 97                             }
 98                             printf("get a new connect...%d\n",new_sock);
 99                             for(i=0;i<_NUM_;i++)
100                             {
101                                 if(fds[i].fd==-1)
102                                 {
103                                     fds[i].fd=new_sock;
104                                     fds[i].events=POLLIN;
105                                     max_fd++;
106                                     break;
107                                 }
108                             }
109                             if(i==_NUM_)
110                             {
111                                 close(new_sock);
112                             }
113                         }
114                         else if((fds[i].fd>0)&&(fds[i].revents==POLLIN))
115                         {
116                             char buf[1024];
117                             ssize_t _s=read(fds[i].fd,buf,sizeof(buf)-1);
118                             if(_s>0)
119                             {
120                                 //read sucess
121                                 buf[_s]='\0';
122                                 printf("client:%s\n",buf);
123                             }
124                             else if(_s==0)
125                             {
126                                 //client shutdown
127                                 printf("client shutdown...\n");
128                                 //
129                                 struct pollfd tmp=fds[i];
130                                 fds[i]=fds[max_fd-1];
131                                 fds[max_fd-1]=tmp;
132                                 //
133                                 close(fds[max_fd-1].fd);
134                                 //
135 
136                                 fds[max_fd-1].fd=-1;
137                                 fds[max_fd-1].events=0;
138                                 fds[max_fd-1].revents=0;
139                                 //easy ignore
140                                 --max_fd;
141                             }
142                             else
143                             {
144                                 perror("read");
145                             }
146                         }
147                         //normal socket
148                         else
149                         {}
150                     }
151                 }
152                 break;
153         }
154     }
155     return 0;
156 }

//poll_client.c
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include<sys/types.h>
  5 #include<sys/socket.h>
  6 #include<netinet/in.h>
  7 #include<arpa/inet.h>
  8 #include<unistd.h>
  9 
 10 void usage(const char* proc)
 11 {   
 12     printf("%s [ip] [port]\n",proc);
 13     exit(1);
 14 }
 15 int main(int argc,char* argv[])
 16 {
 17     if(argc!=3)
 18     {
 19         usage(argv[0]);
 20         exit(1);
 21     }
 22     int server_ip=inet_addr(argv[1]);
 23     int server_port=atoi(argv[2]);
 24 
 25     int client_sock=socket(AF_INET,SOCK_STREAM,0);
 26     if(client_sock<0)
 27     {
 28         perror("socket");
 29         exit(2);
 30     }
 31     struct sockaddr_in server;
 32     server.sin_family=AF_INET;
 33     server.sin_addr.s_addr=server_ip;
 34     server.sin_port=htons(server_port);
 35 
 36     if(connect(client_sock,(struct sockaddr*)&server,sizeof(server))<0)
 37     {
 38         perror("connect");
 39         exit(3);
 40     }
 41     char buf[1024];
 42     while(1)
 43     {
 44         memset(buf,'\0',sizeof(buf));
 45         printf("Please Input: ");
 46         fflush(stdout);
 47         fgets(buf,sizeof(buf)-1,stdin);
 48         if(send(client_sock,buf,sizeof(buf)-1,0)<0)
 49         {
 50             perror("send");
 51             continue;
 52         }
 53         ssize_t _size=recv(client_sock,buf,sizeof(buf)-1,0);
 54         if(_size>0)
 55         {
 56             buf[_size]='\0';
 57             printf("server receive:%s\n",buf);
 58         }
 59     }
 60     return 0;
 61 }
 
 //makefile
  1 .PHONY:all
  2 all:poll_server poll_client
  3 poll_server:poll_server.c
  4     gcc -o $@ $^
  5 poll_client:poll_client.c
  6     gcc -o $@ $^
  7 .PHONY:clean
  8 clean:
  9     rm -f poll_server poll_client
  
  //start.sh
  1 #!/bin/bash
  2 
  3 service iptables stop
  4 ./poll_server 192.168.163.128 8080

運行結果:

wKioL1dzpMKhePBOAABcEol27oU539.png

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