poll使用一個結構體指針代替select中讀事件、寫事件及異常事件。
結構體組成:
events(請求事件)關心該文件描述符的什麼事件
所關心的事件有:
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 }
運行結果:
總結:(select與poll之間的區別)
select:使用三個文件描述符集(分別爲讀事件、寫事件、異常事件)表示它們各自關心什麼事件,將對應的文件描述符設置進去
它存在一定的弊端:
1.每次輪詢前都要對所關心的文件描述符集進行設置及初始化(因爲它們是輸入輸出型參數),每次都要遍歷所有的文件描述符
2.它依賴文件描述符,但是文件描述符集的大小是有一定的限度的,當有大量的客戶端同時請求服務器時不適宜使用
3.它的三個文件描述符集當中若有多個被修改,每次修改都要從用戶態切換到內核態,內核態來負責修改文件描述符的狀態變化
poll:使用一個結構體數組來代替select中的三個文件描述符集,它的大小不受限制
優化:
1.若有需要關心的文件描述符時,將它保存到結構體中,並設置該描述符所關心的事件,每次輪詢完後若有無效的文件描述符時,將其描述符的狀態置-1,當再次調poll函數時會忽略它
2.當有新的文件描述符時,重新遍歷結構體,將出現第一個爲-1的狀態時,將它設置爲要關心的描述符事件狀態,每次有新的文件描述符加入時,更改要關心的文件描述符數量
3.調用poll函數後,結構體中的revents(返回已經就緒的事件)會存儲就緒事件狀態,重新調用poll之前,系統設置該狀態默認其爲0,重新監聽關心事件
但poll也有相應的弊端:當同一時刻有大量客戶端發來請求連接,但有隻有很少的事件處於就緒狀態,因此可能隨着監視描述符數量的增長,效率也會降低