一.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,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的。
转载于:https://blog.51cto.com/10541571/1784067