poll函数
函数原型: int poll(struct pollfd* fds, nfds_t nfds, int timeout);
- 参数1:监听的事件集合
- 参数2:最大的文件描述符+1
- 参数3:超时时间,-1表示阻塞等待
- 返回值:成功,返回触发的监听事件的个数,同select
C语言实现
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <poll.h>
#include <sys/types.h>
#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666
#define MAX_FD 1024
int main(void) {
int lfd, cfd;
int i, len, maxi, nRet, clie_port;
char buf[BUFSIZ], clie_ip[20];
struct sockaddr_in serv_addr, clie_addr;
socklen_t clie_addr_len;
struct pollfd clients[MAX_FD];
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);
lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd == -1) {
perror("socket error");
exit(1);
}
if(bind(lfd, (struct sockaddr*)& serv_addr, sizeof(serv_addr)) == -1) {
perror("bind error");
exit(1);
}
if(listen(lfd, 128) == -1) {
perror("listen error");
exit(1);
}
for(i = 0; i < MAX_FD; ++i) {
clients[i].fd = -1;
}
clients[0].fd = lfd; //第一个事件设置为服务端套接字描述符的读事件
clients[0].events = POLLIN;
maxi = 0;
while(1) {
nRet = poll(clients, maxi+1, -1);
if(clients[0].revents & POLLIN) { //读事件,有客户端发起连接
clie_addr_len = sizeof(clie_addr);
cfd = accept(lfd, (struct sockaddr*)&clie_addr, &clie_addr_len); //连接客户端
if(cfd == -1) {
perror("accept error");
exit(1);
}
for(i = 0; i < MAX_FD; ++i) {
if(clients[i].fd == -1) { //加入到监听事件集合
clients[i].fd = cfd;
clients[i].events = POLLIN;
clie_port = ntohs(clie_addr.sin_port);
inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_ip, clie_addr_len);
printf("%s:%d connected\n", clie_ip, clie_port);
break;
}
}
if(i == MAX_FD) {
printf("事件数超出上限\n");
exit(1);
}
maxi = maxi < i? i: maxi;
--nRet;
}
for(i = 1; i <= maxi && nRet > 0; ++i) {
if(clients[i].fd == -1) continue;
if(clients[i].revents & POLLIN) { //读就绪事件产生,处理客户端请求
cfd = clients[i].fd;
--nRet;
len = read(cfd, buf, sizeof(buf));
if(len < 0) {
perror("read error");
exit(1);
}
else if(len == 0) { //客户端退出,将对应的事件清除,即置对应fd为-1,并关闭文件描述符
printf("一个客户端退出了!\n");
clients[i].fd = -1;
close(cfd);
}
else {
write(cfd, buf, len); //回显到客户端
buf[len] = '\0';
printf("收到一条新消息:%s\n", buf); //在服务端显示新消息
}
}
}
}
close(lfd);
return 0;
}
注意上述程序是在linux下运行的c程序,无客户端代码,旨在了解poll的使用,可通过nc 127.1 6666 命令来模拟客户端并连接服务器来验证。
总结
- poll同select相同,也需要客户端到内核以及内核到客户端的拷贝开销。
- poll能够突破文件描述符的限制。
- poll同select相同,也需要遍历整个集合。
- poll不需要重新设定监听集合,即监听集合得到了复用。