一.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;
程序結果:
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 }
程序結果:
poll總結:
1.poll與select一樣實現需要自己不斷輪詢所有fd集合,直到設備就緒,期間可能要睡眠和喚醒多次交替。
2.poll與select一樣每次調用都要把fd集合從用戶態往內核態拷貝一次。
3.select,poll都是IO多路複用的機制。I/O多路複用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。但select,poll本質上都是同步I/O,因爲他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的。