poll 多路复用
1.select和poll的异同
select()和poll()系统调用的本质一样,前者在BSD UNIX中引入的,后者在System V中引入的。poll()的机制与 select() 类 似,与 select() 在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是 poll() 没有最大文件 描述符数量的限制(但是数量过大后性能也是会下降)。poll() 和 select() 同样存在一个缺点就是,包含大量文件描述符的数组 被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增 大。
2.函数原型及解析
函数原型:
#include <poll.h>
struct pollfd
{
int fd; /* 文件描述符 */
short events; /* 等待的事件 */
short revents; /* 实际发生了的事件 */
} ;
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
解析:
- 第一个参数用来指向一个struct pollfd类型的数组,每一个pollfd结构体指定了一个被监视的文件描述符,指示poll()监视多个文 件描述符。每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操作结果 事件掩码,内核在调用返回时设置这个域,events域中请求的任何事件都可能在revents域中返回。下表列出指定 events 标志以 及测试 revents 标志的一些常值:

POLLIN | POLLPRI等价于select()的读事件。
POLLOUT |POLLWRBAND等价于select()的写事件。 POLLIN等价于 POLLRDNORM |POLLRDBAND。POLLOUT则等价于POLLWRNORM。例如,要同时监视一个文件描述符是否可读和可 写,我们可以设置 events为POLLIN |POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的 events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而 不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。 - 第二个参数 nfds 指定数组中监听的元素个数;
- 第三个参数 timeout指定等待的毫秒数,无论I/O是否准备好,poll都会返回。timeout指定为负数值表示无限超时,使poll() 一直挂起直到一个指定事件发生;timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件。 这种情况下,poll()就像它的名字那样,一旦选举出来,立即返回。
该函数成功调用时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0; 失败时,poll()返回-1,并设置errno为下列值之一:
- EBADF 一个或多个结构体中指定的文件描述符无效。
- EFAULTfds 指针指向的地址超出进程的地址空间
- EINTR 请求的事件之前产生一个信号,调用可以重新发起。
- EINVALnfds 参数超出PLIMIT_NOFILE值。
- ENOMEM 可用内存不足,无法完成请求。
3.代码示例
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<string.h>
5 #include<errno.h>
6 #include<ctype.h>
7 #include<time.h>
8 #include<pthread.h>
9 #include<getopt.h>
10 #include<libgen.h>
11 #include<sys/types.h>
12 #include<sys/socket.h>
13 #include<netinet/in.h>
14 #include<poll.h>
15 #include<arpa/inet.h>
16
17 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
18
19 static inline void print_usage(char *progname);
20 int socket_server_init(char *listen_ip, int listen_port);
21
22 int main(int argc, char **argv)
23 {
24 int listenfd, connfd;
25 int serv_port = 0;
26 int daemon_run = 0;
27 int opt;
28 char *progname = NULL;
29 int rv, i, j;
30 int found;
31 int max;
32 char buf[1024];
33 struct pollfd fds_array[1024];
34
35 struct option long_options[] =
36 {
37 {"daemon", no_argument, NULL, 'b'},
38 {"port", required_argument, NULL, 'p'},
39 {"help", no_argument, NULL, 'h'},
40 {NULL, 0, NULL, 0}
41 };
42
43 progname = basename(argv[0]);
44
45 while((opt = getopt_long(argc,argv,"dp:h",long_options,NULL))!=-1)
46 {
47 switch(opt)
48 {
49 case 'b':
50 daemon_run = 1;
51 break;
52
53 case 'p':
54 serv_port = atoi(optarg);
55 break;
56
57 case 'h':
58 print_usage(progname);
59 return EXIT_SUCCESS;
60
61 default:
62 break;
63
64 }
65 }
66
67 if(!serv_port)
68 {
69 print_usage(progname);
70 return -1;
71 }
72
73 if((listenfd = socket_server_init(NULL, serv_port)) < 0)
74 {
75 printf("ERROR:%s server listen on port%d failture\n",argv[0],serv_port);
76 return -2;
77 }
78 printf("%s progname start to on port %d\n", argv[0], serv_port);
79
80 if(daemon_run)
81 {
82 daemon(0,0);
83 }
84
85 for(i=0; i<ARRAY_SIZE(fds_array); i++)
86 {
87 fds_array[i].fd = -1;
88 }
89 fds_array[0].fd = listenfd;
90 fds_array[0].events = POLLIN;
91
92 max = 0;
93
94 for(;;)
95 {
96 rv = poll(fds_array, max+1, -1);
97 if(rv<0)
98 {
99 printf("select failture:%s\n", strerror(errno));
100 break;
101 }
102 else if(rv = 0)
103 {
104 printf("select get timeout");
105 continue;
106 }
107
108 if(fds_array[0].events & POLLIN)
109 {
110 if((connfd=accept(listenfd, (struct sockaddr *)NULL, NULL))<0)
111 {
112 printf("accept new client failture:%s\n",
113 strerror(errno));
114 continue;
115 }
116 found = 0;
117 for(i = 1; i < ARRAY_SIZE(fds_array); i++)
118 {
119 if(fds_array[i].fd<0)
120 {
121 printf("accept new client[%d]andadditintoarr\n"
122 ,connfd);
123 fds_array[i].fd = connfd;
124 fds_array[i].events = POLLIN;
125 found = 1;
126 break;
127 }
128 }
129
130 if(!found)
131 {
132 printf("accept new client[%d] but full, so refuse it\n"
133 ,connfd);
134 close(connfd);
135 continue;
136 }
137
138 max = i > max ? i : max;
139 if(--rv <= 0)
140 continue;
141 }
142 else
143 {
144 for(i=1; i<ARRAY_SIZE(fds_array); i++)
145 {
146 if(fds_array[i].fd < 0)
147 continue;
148 if((rv=read(fds_array[i].fd, buf, sizeof(buf)))<=0)
149 {
150 printf("socket[%d] read failture or get disconncet.\n",fds_array[i].fd);
151 close(fds_array[i].fd);
152 fds_array[i].fd = -1;
153 }
154 else
155 {
156 printf("socket[%d] read get %d bytes data\n",
157 fds_array[i].fd,rv);
158
159 for(j=0; j<rv; j++)
160 buf[j]=toupper(buf[j]);
161
162 if(write(fds_array[i].fd, buf, rv)<0)
163 {
164 printf("socket[%d] write failture:%s\n",
165 fds_array[i].fd,strerror(errno));
166 close(fds_array[i].fd);
167 fds_array[i].fd = -1;
168 }
169 }
170 }
171 }
172
173 }
174 Cleanup:
175 close(listenfd);
176 return 0;
177 }
178
179 static inline void print_usage(char *progname)
180 {
181 printf("Usage:%s [OPTION]...\n", progname);
182 //printf("%s is a socket server progname, which used to verify client and echo back string from it\n", progname);
183 return;
184 }
185
186 int socket_server_init(char *listen_ip, int listen_port)
187 {
188 struct sockaddr_in servaddr;
189 int rv=0;
190 int on=1;
191 int listenfd;
192
193 if((listenfd = socket(AF_INET,SOCK_STREAM,0))<0)
194 {
195 printf("Use socket() to create a TCP socket failture:%s\n", strerror(errno));
196 return -1;
197 }
198
199 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
200
201 memset(&servaddr, 0, sizeof(servaddr));
202 servaddr.sin_family = AF_INET;
203 servaddr.sin_port = htons(listen_port);
204
205 if(!listen_ip)
206 {
207 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
208 }
209 else
210 {
211 if(inet_pton(AF_INET, listen_ip,&servaddr.sin_addr)<=0)
212 {
213 printf("inet_pton() set listen IP address failture.\n");
214 rv = -2;
215 goto Cleanup;
216 }
217 }
218
219 if(bind(listenfd, (struct sockaddr *) &servaddr,sizeof(servaddr))<0)
220 {
221 printf("Use bind() to bind the TCP socket failture:%s\n",strerror(errno));
222 rv = -4;
223 goto Cleanup;
224 }
225
226 Cleanup:
227 if(rv<0)
228 close(listenfd);
229 else
230 rv= listenfd;
231
232 return rv;
233 }
poll的流程图和select的流程图基本相同,这里就不做阐述。
本文对比了poll和select两种多路复用技术的异同,详细解析了poll函数的原型和内部机制,通过代码示例展示了如何使用poll进行高效的网络编程。
2294

被折叠的 条评论
为什么被折叠?



