poll函数与select原理类似,只是poll没有为每个状态(可读、可写、异常)构造一个描述符集,而是构造了一个pollfd结构体数组,每个数组元素指定一个描述符编号以及对其关心的状态。
参数2:nfds 是nfds_t类型的参数,用于标记结构体数组fds的数组元素的总数量;
参数3:timeout是poll函数阻塞的时间,单位:毫秒;
1、0:poll 函数立即返回而不阻塞;
2、INFTIM:即负数,那么poll函数会一直阻塞下去,直到所检测的描述符上所关心的事件发生;
3、timeout指定时间:poll()函数会以轮询方式在timeout所指定的毫秒时间之后返回;
返回值:
1、成功:返回数组fds中已就绪的读、写或出错状态的描述符的总数量;
2、timeout超时:返回0;
3、失败:-1;
poll函数的特点:每当调用这个函数之后,系统不会清空fds这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select函数不同,调用select函数之后,select函数会清空它所检测的描述符集合,导致每次调用select之前都必须把描述符重新加入到待检测的集合中;因此,select函数适合于只检测一个描述符的情况,而poll函数适合于大量描述符的情况。虽然pollfd并没有最大数量限制,但是select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的文件描述符。事实上,同时连接的大量客户端在同一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。
使用poll实现一个tcp服务器
//server端:
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/poll.h>
#include<stdlib.h>
#define _SIZE 64
int array_rfds[1024];
static void Usage(const char* proc)
{
printf("Usage:%s [local_ip][local_port]\n",proc);
}
int Startup(const char* _ip,int _port)
{
int sock= socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
exit(1);
}
int flag = 1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port= htons(_port);
server.sin_addr.s_addr = inet_addr(_ip);
if(bind(sock,(struct sockaddr*)&server,sizeof(server))<0)
{
perror("bind");
exit(2);
}
if(listen(sock,10)<0)
{
perror("listen");
exit(3);
}
return sock;
}
int main(int argc,const char* argv[])
{
if(argc!=3)
{
Usage(argv[0]);
return 1;
}
int listen_sock = Startup(argv[1],atoi(argv[2]));
struct pollfd _pollfd[_SIZE];
_pollfd[0].fd = listen_sock;
_pollfd[0].events = POLLIN;
_pollfd[0].revents = 0;
int timeout = 0;
int i = 1;
int max_num = 1;
for(;i <_SIZE;i++)
{
_pollfd[i].fd = -1;
}
while(1)
{
timeout = 5000;
switch(poll(_pollfd,max_num,timeout))
{
case 0:
printf("timeout...\n");
break;
case -1:
perror("poll");
break;
default:
{
for(i = 0;i<max_num;++i)
{
if(_pollfd[i].fd == listen_sock &&\
_pollfd[i].revents & POLLIN)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_sock = accept(listen_sock,\
(struct sockaddr*)&client,&len);
if(new_sock<0)
{
perror("accept");
continue;
}
printf("new client--> %s:%d\n",\
inet_ntoa(client.sin_addr),\
ntohs(client.sin_port));
int j =1;
for(;j<64;++j)
{
if(_pollfd[j].fd == -1)
{
_pollfd[j].fd = new_sock;
_pollfd[j].events = POLLIN;
_pollfd[j].revents = 0;
break;
}
}
if(j == _SIZE)
{
close(new_sock);
}
if(j == max_num)
max_num++;
}
else if(_pollfd[i].fd != listen_sock && _pollfd[i].revents&POLLIN)
{
char buf[1024];
ssize_t s= read(_pollfd[i].fd,buf,sizeof(buf)-1);
if(s>0)
{
buf[s] = 0;
printf("client say: %s\n",buf);
}
else if(s ==0)
{
printf("client is quit!!\n");
close(_pollfd[i].fd);
_pollfd[i].fd = -1;
}
else
{
perror("read");
close(_pollfd[i].fd);
_pollfd[i].fd = -1;
}
}
}
}
break;
}
}
for(i=0;i<_SIZE;++i)
{
if(_pollfd[i].fd !=-1)
close(_pollfd[i].fd);
}
return 0;
}
//client端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void usage(const char* proc)
{
printf("Usage:%s[server_ip][server_port]\n",proc);
}
int main(int argc,char *argv[])
{
if(argc != 3)
{
usage(argv[0]);
return 1;
}
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
return 2;
}
struct sockaddr_in peer;
peer.sin_family = AF_INET;
peer.sin_port = htons(atoi(argv[2]));
peer.sin_addr.s_addr = inet_addr(argv[1]);
int ret = connect(sock,(struct sockaddr*)&peer,sizeof(peer));
if(ret<0)
{
perror("connect");
printf("%s\n",strerror(ret));
return 3;
}
char buf[1024];
while(1)
{
printf("please enter: ");
fflush(stdout);
ssize_t s=read(0,&buf,sizeof(buf));
if(s<0)
{
perror("read");
return 4;
}
buf[s-1]=0;
write(sock,&buf,strlen(buf));
printf("server echo: %s\n",buf);
}
close(sock);
return 0;
}
poll模型的优点:
1、相对于select模型来说,高效。每当调用poll函数之后,系统不会清空fds这个数组,操作起来比较方便;特别是对于文件描述符连接比较多的情况下,在一定程度上可以提高处理的效率。
2、处理了select模型监视文件句柄有上限的问题;
poll模型的缺点:虽然pollfd并没有最大数量限制,但是select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的文件描述符。事实上,同时连接的大量客户端在同一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。