poll采用了一个单独的结构体pollfd数组,由fds指针指向这个组。pollfd结构体定义如下:
struct pollfd {
int fd; //文件描述符
short events; //当下文件描述符关心的事件
short revents; //文件描述符关心的事件就绪
};
每一个pollfd结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。每个结构体的events域是监视该文件描述符关心的事件,由用户来设置这个域。revents域是文件描述符的关心事件发生了。内核在调用返回时设置这个域。events域中请求的任何事件都可能在revents域中返回。
函数原型:int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);
参数依次为: 结构体数组、有效文件描述符个数、超时时间
poll优点
1、poll() 不要求开发者计算最大文件描述符加一的大小。
2、poll() 在应付大数目的文件描述符的时候相比于select速度更快 。
3、它没有最大连接数的限制,原因是它是基于链表来存储的。
poll缺点
1、大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
2、与select一样,poll返回后,需要轮询pollfd来获取就绪的描述符。
使用poll监控输入输出
#include<stdio.h>
#include<poll.h>
#include<stdlib.h>
#include<string.h>
int main(int argc,char *argv[])
{
struct pollfd _poll[1];
_poll[0].fd=0;
_poll[0].events=POLLIN;
_poll[0].revents=0;
int timeout=3000;
int i=0;
char buf[1024];
while(1)
{
switch(poll(_poll,1,timeout))
{
case 0:
printf("timeout");
break;
case -1:
printf("poll");
break;
default:
{
for(i=0;i<2;i++)
{
if((_poll[0].fd==0)&&(_poll[0].revents)&POLLIN)
{
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s]=0;
if(strncmp(buf,"hello poll",10)==0)
{
close(_poll[i].fd);
return 1;
}
printf("ecjo:%s\n",buf);
}
}
}
}
break;
}
}
return 0;
}
使用poll实现一个服务器;
#include<stdio.h>
#include<stdlib.h>
#include<poll.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
static void usage(char *proc)
{
printf("usage:%s [local_ip] [local_port]\n",proc);
}
int startup(char* _ip,int _port)
{
//create socket
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
return 2;
}
//port multiplexing
int flg=1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&flg,sizeof(flg));
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_addr.s_addr=inet_addr(_ip);
local.sin_port=htons(_port);
//bind
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
return 3;
}
//listen
if(listen(sock,10)<0)
{
perror("listen");
return 4;
}
return sock;
}
int main(int argc,char *argv[])
{
if(argc!=3)
{
usage(argv[0]);
return 1;
}
int listen_sock=startup(argv[1],atoi(argv[2]));
struct pollfd fd_set[2];
fd_set[0].fd=listen_sock;
fd_set[0].events=POLLIN;
fd_set[0].revents=0;
int timeout=2000;
int n=sizeof(fd_set)/sizeof(fd_set[0]);
struct sockaddr_in client;
socklen_t len=sizeof(client);
int i=1;
for(;i<n;i++)
{
fd_set[i].fd=-1;
}
int maxfd=0;
while(1)
{
switch(poll(fd_set,maxfd+1,timeout))
{
case 0:
printf("timeout..\n");
break;
case -1:
printf("poll");
break;
default:
{
for(i=0;i<n;i++)
{
if((fd_set[i].fd==listen_sock)&& \
(fd_set[i].revents)&POLLIN)
{
int new_sock=accept(listen_sock,\
(struct sockaddr*)&client,&len);
if(new_sock<0)
{
perror("accept");
continue;
}
printf("get a new client\n");
int j=0;
for(j=0;j<n;j++)
{
if(fd_set[j].fd==-1)
{
fd_set[j].fd=new_sock;
fd_set[j].events=POLLIN;
fd_set[j].revents=0;
break;
}
}
if(j==n)
{
close(new_sock);
}
if(j>maxfd)
{
maxfd=j;
}
}
else if((fd_set[i].fd>0)&& (fd_set[i].revents)&POLLIN)
{
char buf[1024];
ssize_t s=read(fd_set[i].fd,buf,sizeof(buf)-1);
if(s>0)
{
buf[s]='\0';
printf("client: %s\n",buf);
write(fd_set[i].fd,buf,strlen(buf));
}
else if(s==0)
{
close(fd_set[i].fd);
int p=1;
for(;p<n;i++)
{
if((fd_set[p].fd!=-1)&&(p!=i))
{
int temp=fd_set[i].fd;
fd_set[i].fd=fd_set[p].fd;
fd_set[p].fd=temp;
}
}
}
}
}
}
break;
}
}
return 0;
}