一.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,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的。
本文出自 “输出菱形图案” 博客,请务必保留此出处http://10541571.blog.51cto.com/10531571/1784067
原文:http://10541571.blog.51cto.com/10531571/1784067