poll()函数:这个函数是某些Linux系统提供的用于执行与select()函数同等功能的函数,下面是这个函数的声明:
#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
参数说明:
fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符;每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况;
nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;
timeout:是poll函数调用阻塞的时间,单位:毫秒;
返回值:
0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;
==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒;换句话说,如果所检测的socket描述符上没有任何事件发生的话,那么poll()函数会阻塞timeout所指定的毫秒时间长度之后返回,如果timeout==0,那么poll() 函数立即返回而不阻塞,如果timeout==INFTIM,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;
-1: poll函数调用失败,同时会自动设置全局变量errno;
poll()接受一个指向结构struct pollfd列表的指针,其中包括了你想监测的文件描述符和事件,结构体如下:
struct pollfd {
int fd; /*文件描述符*/
short events; /* 等待的需要测试事件 */
short revents; /* 实际发生了的事件,也就是返回结果 */
};
poll与select不同,通过一个pollfd数组向内核传递需要关注的事件,故没有描述符个数的限制,
pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组只需要被初始化一次。
poll的实现机制与select类似,其对应内核中的sys_poll,只不过poll向内核传递pollfd数组,然后对pollfd中的每个描述符进行poll,相比处理fdset来说,poll效率更高。
poll返回后,需要对pollfd中的每个元素检查其revents值,来得指事件是否发生。
poll的优点:
1)poll() 不要求开发者计算最大文件描述符加一的大小。
2)poll() 在应付大数目的文件描述符的时候相比于select速度更快
3)它没有最大连接数的限制,原因是它是基于链表来存储的。
poll的缺点:
1)大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
2)与select一样,poll返回后,需要轮询pollfd来获取就绪的描述符
利用poll编写基于TCP的服务器:
服务器端代码:
#include<stdio.h>
#include<poll.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdlib.h>
static void use(char* proc)
{
printf("use:%s [local_ip][local_port]",proc);
}
int startup(const char* ip,int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
return 2;
}
int opt = 1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = inet_addr(ip);
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
return 3;
}
if(listen(sock,10)<0)
{
perror("listen");
return 4;
}
return sock;
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
use(argv[0]);
return 1;
}
int listen_sock = startup(argv[1],atoi(argv[2]));
struct pollfd _poll[64];
_poll[0].fd = listen_sock;
_poll[0].events = POLLIN;
_poll[0].revents = 0;
//int poll_len = sizeof(_poll)/sizeof(_poll[0]);
int i = 1;
for(;i<64;i++)
{
_poll[i].fd = -1;
}
int maxfd = 1;
while(1)
{
int timeout = 5000;
switch( poll(_poll,maxfd,timeout))
{
case -1:
perror("poll");
break;
case 0:
printf("timeout\n");
break;
default:
{
int i = 0;
for(;i<maxfd;i++)
{
if(_poll[i].fd==listen_sock&&_poll[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("get a new client:%s:%d\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));
int j =1;
for(;j<64;j++)
{
if(_poll[j].fd==-1)
{
_poll[j].fd = new_sock;
_poll[j].events = POLLIN;
_poll[j].revents = 0;
break;
}
}
if(j==64)
{
close(new_sock);
}
if(j==maxfd)
{
maxfd++;
}
}//if
else if(_poll[i].fd != listen_sock && _poll[i].revents & POLLIN)
{
char buf[1024];
ssize_t s = read(_poll[i].fd,buf,sizeof(buf)-1);
if(s>0)
{
buf[s] = 0;
printf("client say#%s\n",buf);
}
else if(s<0)
{
perror("read");
close(_poll[i].fd);
_poll[i].fd = -1;
}
else if(s==0)
{
printf("client quit!\n");
close(_poll[i].fd);
_poll[i].fd = -1;
}
}
}//for
}
break;
}
}
for(i=0;i<64;++i)
{
if(_poll[i].fd!=-1)
close(_poll[i].fd);
}
return 0;
}
客户端代码:
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
static void Use(const char* proc)
{
printf("%s [server_ip] [server_port]\n",proc);
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
Use(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]);
if(connect(sock,(struct sockaddr*)&peer,sizeof(peer))<0)
{
perror("connect");
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;
}
运行结果示例: