poll函数其实和select函数没有太大区别,与select不同的是函数接口,poll不为每种状态设置对应的文件描述符集,而是设置一个struct pollfd类型的结构体数组,对其数组元素设置文件描述符和对其所关心的状态。
优点:自带数组结构,并且将监听事件集合和返回事件集合分离,可以扩展监听上限,突破1024上限。
缺点:不能跨平台
函数原型为:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds为结构体指针,指向一个结构体数组,其结构体内容为:
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
events指定监听文件描述符的事件,其值一般可设置为POLLIN,POLLOUT,POLLERR,分别表述读,写,发生错误。
revents也有POLLIN,POLLOUT,POLLERR三种值,不过其代表着文件描述符操作结果事件.
nfds这个参数我看网上都说它是第一个参数所指向的数组实际元素个数,但是在实际编程中却不能传实际个数,而应该传 最大下标+1,否则会出错。这块我还没弄清楚原因。
timeout参数:
值为0时,设置非阻塞
值为-1时,设置阻塞
值大于0时,设置等待时间,单位为毫秒级
返回值:成功返回设置监听文件描述符中发生对应事件的总个数,失败返回-1,并设置errno
对其用法和select函数一样,直接给出代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<arpa/inet.h>
5 #include<sys/types.h>
6 #include<sys/socket.h>
7 #include<ctype.h>
8 #include<poll.h>
9 #define SERVER_PORT 7777
10 void perr_exit(const char *p)
11 {
12 perror(p);
13 exit(1);
14 }
15 int main(void)
16 {
17 int lfd,cfd,ret,maxi;
18 struct sockaddr_in serv_addr,client_addr;
19 socklen_t client_addr_len = sizeof(client_addr);
20 char buf[BUFSIZ];
21 struct pollfd pfd[1024];
22
23 lfd = socket(AF_INET,SOCK_STREAM,0);
24 if(lfd == -1)
25 perr_exit("socket error");
26
27 //绑定ip和port
28 serv_addr.sin_family = AF_INET;
29 serv_addr.sin_port = htons(SERVER_PORT);
30 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
31 ret = bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
32 if(ret == -1)
33 perr_exit("bind error");
34
35 listen(lfd,128);
36
37 for(int i = 0;i < 1024;i++)//初始化pfd数组里的文件描述符
38 pfd[i].fd = -1;
39 pfd[0].fd = lfd;//将监听的第一个文件描述符,存入pfd[0]中
40 pfd[0].events = POLLIN;//lfd监听普通读事件
41 pfd[0].revents = 0;
42 maxi = 0;//存放文件描述符的数组最大下标
43 while(1)
44 {
45 ret = poll(pfd,maxi+1,-1);//阻塞监听客户端请求
46 if(ret == -1)
47 perr_exit("poll error");
48 if(pfd[0].revents & POLLIN)//判断其事件是否为读事件
49 {
50 cfd = accept(lfd,(struct sockaddr
51 *)&client_addr,&client_addr_len);//接受客户端连接请求accept不会阻塞
52 if(cfd == -1)
53 perr_exit("accept error");
54 for(int i = 1;i < 1024;i++)//将文件描述符存放在pfd数组中的空闲位置
55 {
56 if(pfd[i].fd == -1)
57 {
58 pfd[i].fd = cfd;
59 pfd[i].events = POLLIN;//设置其为普通读事件
60 pfd[i].revents = 0;
61 if(maxi < i)//保存最大下标
62 maxi = i;
63 break;
64 }
65 }
66 if(--ret == 0)//若处理完事件返回poll继续阻塞监听,不用执行后面代码
67 continue;
68 }
69 for(int i = 1;i <= maxi ;i++)
70 {
71 if(pfd[i].fd == -1)//跳过文件描述符为空的数组单元
72 continue;
73 if(pfd[i].revents & POLLIN)//若文件描述符有普通读事件发生则处理
74 {
75 int n = read(pfd[i].fd,buf,sizeof(buf));
76 if(n < 0)
77 perr_exit("read error");
78 else if(n == 0)//n == 0说明对端套接字关闭则关闭文件描述符,并将其从fds数组中去除
79 {
80 close(pfd[i].fd);
81 pfd[i].fd = -1;
82 continue;
83 }
84 for(int i = 0 ;i < n;i++)
85 buf[i] = toupper(buf[i]);
86 write(pfd[i].fd,buf,n);
87 if(--ret == 0)//若处理完发生事件的文件描述符则不需要往后遍历,返回到poll处阻塞监听
88 break;
89 }
90 }
91 }
92 return 0;
93 }