函数结构
int poll(struct pollfd* fds,nfds_t nfds,int timeout);
参数:
fds : 这是一个poll函数监听的结构列表,每一个元素中,包含以下的内容 (文件描述符、监听的事件集合、返回的事件集合)
nfds : 表示fds数组的长度
timeout : 这里是整型,单位就是毫秒, timeout为0,-1,或者其他整数和select的含义是一模一样的;
返回值: 和select一模一样
0 : 函数等待超时
-1 : 出错
一个正整数 : 监听文件描述符就绪返回
events和revents
events: 关注事件(读就绪/写就绪/异常),输入的时候起作用
revents: 输出结果,输出的时候起作用
输入输出参数分离,并且每次调用的时候不需要重新设置
取值:(部分)
事件 |
描述 |
是否可作为输入 |
是否可作为输出 |
POLLIN |
数据(包括普通数据和优先数据)可读 |
是 |
是 |
POLLOUT |
数据(包括普通数据和优先数据)可写 |
是 |
是 |
POLLRDNORM |
普通数据可读 |
是 |
是 |
POLLRDBAND |
优先级带数据可读(Linux不支持) |
是 |
是 |
POLLPRI |
高优先级数据可读,比如TCP带外数据 |
是 |
是 |
poll优点
select使用了三个位图来表示三个fdset的方式,poll使用一个pollf指针实现
poll里包含了要监视的event和发生的event,使读写分离,每次循环不需要重新设置,接口使用方便
poll的文件描述符数量不受限制,一个数组可以开多大,就可以有多少个文件描述符,换句话说,只要内存足够,想开多少就开多少,但是太多了性能也会下降
poll缺点
当文件描述符很多的时候,依旧需要使用轮询的方式来获取文件描述符,效率低
每次调用poll也需要吧大量的pollfd结构从用户态拷贝至内核中,文件描述符很多事,效率也会很低
同时连接的大量客户在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会现行下降
代码1.用poll监视标准输入
#include
#include
#include
int main()
{
struct pollfd fds;
fds.fd = 0;
fds.events = POLLIN;
while(1)
{
int ret = poll(&fds,1,123456);
if(ret < 0)
{
perror("poll");
continue;
}
if(ret == 0)
{
printf("timeout...\n");
continue;
}
if(fds.revents == POLLIN)
{
char buf[1024] = {0};
read(0,buf,sizeof(buf)-1);
printf("stdin: %s\n",buf);
}
}
return 0;
}
结果演示:
代码2.多路转接服务器
// poll_server.c
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct pollfd pollfd;
int ServerStart(short port)
{
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0)
{
perror("socket");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
{
perror("bind");
return -2;
}
if(listen(fd,5) < 0)
{
perror("listen");
return -3;
}
return fd;
}
void Init(pollfd* fd_list,int size)
{
int i = 0;
for(; i < size; ++i)
{
fd_list[i].fd = -1;
fd_list[i].events = 0;
fd_list[i].revents = 0;
}
}
void Add(int fd,pollfd* fd_list,int size)
{
int i = 0;
for(; i < size; ++i)
{
// 找到第一个空位将fd加入进去
if(fd_list[i].fd == -1)
{
fd_list[i].fd = fd;
fd_list[i].events = POLLIN;
break;
}
}
}
int main(int argc, char* argv[])
{
if(argc != 2)
{
printf("Usage: %s [port]\n",argv[0]);
return 1;
}
int listen_sock = ServerStart(atoi(argv[1]));
if(listen_sock < 0)
{
printf("ServerStart Faild\n");
return 1;
}
pollfd fd_list[1024];
Init(fd_list,sizeof(fd_list)/sizeof(pollfd));
Add(listen_sock,fd_list,sizeof(fd_list)/sizeof(pollfd));
// 进入事件循环
while(1)
{
int ret = poll(fd_list,sizeof(fd_list)/sizeof(pollfd),123456);
if(ret < 0)
{
perror("poll");
continue;
}
else if(ret == 0)
{
printf("time out...\n");
continue;
}
size_t i = 0;
for(; i < sizeof(fd_list)/sizeof(pollfd); ++i)
{
if(fd_list[i].fd == -1)
{
continue;
}
if(!(fd_list[i].revents & POLLIN)) //如果输出参数不是pollin,就跳过
{
continue;
}
if(fd_list[i].fd == listen_sock)
{// 处理listen_sock情况
// 证明客户端已经连接上了,可以进行accept了
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_sock = accept(fd_list[i].fd,(struct sockaddr*)&client,&len);
if(new_sock < 0)
{
perror("accept");
continue;
}
Add(new_sock,fd_list,sizeof(fd_list)/sizeof(pollfd));
printf("[client: %d] connect\n",new_sock);
}
else
{// new_sock已经就绪
// 可以进行读写了
char buf[1024] = {0};
ssize_t read_size = read(fd_list[i].fd,buf,sizeof(buf)-1);
if(read_size < 0)
{
perror("read");
continue;
}
if(read_size == 0)
{
printf("[client: %d] disconnect\n",fd_list[i].fd);
fd_list[i].fd = -1;
fd_list[i].events = 0;
fd_list[i].revents = 0;
close(fd_list[i].fd);
}
printf("[client: %d] : %s\n",fd_list[i].fd,buf);
write(fd_list[i].fd,buf,strlen(buf)); // 回显
} // end else
} // end for(; i < sizeof(fd_list)/sizeof(pollfd); ++i)
} // end while(1)
return 0;
}
客户端代码和select客户端代码一致,这里就不写了,需要的朋友可以移步☞ I/O多路复用 - select