函数功能:在一段在指定的时间内,监听用户感兴趣的可读、写、异常事件
函数原型:int poll(struct pollfd* fds,nfds_t* nfds,int timeout)
fds:指向所有感兴趣的文件描述符上的可写、可读、异常事件,它是一个结构体的数组类型,每个格子都包含以下内容:
struct pollfd
{
int fd;//文件描述符
short events;//注册的事件
short revents//实际就绪的事件,由内核填充
};
poll支持的事件类型:
POLLIN | 数据可读 |
POLLRDNORM | 普通数据可读 |
POLLRDBAND | 优先级带数据可读 (Linux下不支持) |
POLLOUT | 数据可写 |
POLLWRNORM | 普通数据可读 |
POLLWRBAND | 优先级带数据可写 |
nfds:表示sizeof(nfds)
timeout:表示poll的超时时间
poll所能监听的文件描述符个数为65535
函数功能实现过程
(1)应用程序通过poll函数的pollfd结构体将感兴趣的文件描述符和事件类型
(2)调用poll系统调用,将pollfd类型的结构体数组拷贝给内核空间,内核程序采用轮询方式查找所传入的数组中的就绪文件描述符,将内核处理之后的结果通过revents带回,这里的revents中不但有就绪的文件描述符,也有未就绪的,比select有优势的地方是内核并没有直接在传进的数组中修改状态,而是将传入的数组拷贝一份进行修改并由revents带回,所以下一次调用时不需要重新设置要监听的文件描述符;但是采用轮询方式时间复杂度为O(n)
(3)应用程序接受到内核返回的pollfd之后,同样采用轮询方式将events与revents遍历查找,时间复杂度为O(n),找出就绪的文件描述符
poll相对于select的优势
poll所能监听的文件描述符数达到系统允许打开的最大文件描述符数,而select由于_FD_SETSIZE的限制只能达到1023个
poll函数的缺点
(1)应用程序和内核程序都采用轮询方式查找就绪文件描述符,时间复杂度为O(n),
(2)poll仍需要每次调用将文件描述符数组拷贝一份给内核空间
(3)poll之工作在效率低下的LT模式下,在未处理就绪事件时会一直提醒就绪信息
简单poll使用
#define MAX 128
void Init(struct pollfd*fds,int len)
{
int i = 0;
for(; i < len ;++i)
{
fds[i].fd = -1;
fds[i].events = 0;
}
}
void Addfd(struct pollfd*fds,int len,int fd)
{
int i = 0;
for(; i < len ;++i)
{
if(fds[i].fd == -1)
{
fds[i].fd = fd;
fds[i].events = POLLIN;
break;
}
}
}
void main()
{
int listen_fd = socket(AF_INET,SOCK_STREAM,0);//创建监听套接字
assert(listen_fd != -1);
struct sockaddr_in ser,cli;//在绑定函数中需要的结构体,用来记录客户端的iip地址和端口号
ser.sin_family = AF_INET;//地址族:TCP/IP
ser.sin_port = htons(6000);//将客户端端口号转化为网络字节序
ser.sin_addr.s_addr = inet_addr("127.0.0.1");//将客户端的ip地址转化为网络字节序;注意这里输入的ip地址是链接本机;
int ret = bind(listen_fd,(struct sockaddr*)&ser,sizeof(ser));//绑定监听套接字
assert(ret != -1);
listen(listen_fd,5);//监听
printf("listen finish\n");
struct pollfd fds[MAX];
Init(fds,MAX);
Addfd(fds,MAX,listen_fd);
while(1)
{
int n = poll(fds,MAX,-1);
assert(n != -1);
if( n == 0)
{
printf("TIME OUT\n");
continue;
}
int i = 0;
for( ; i < MAX ;++i)
{
if(fds[i].fd == -1)
{
continue;
}
if(fds[i].revents & POLLIN)
{
int fd = fds[i].fd;
if(fd == listen_fd)
{
int len = sizeof(cli);
int c = accept(fd,(struct sockaddr*)&cli,&len);
assert(c != -1);
printf("CLI IS SUCCESS\n");
Addfd(fds,MAX,c);
}
else
{
char buff[128] = {0};
int n = recv(fd,buff,127,0);
if(n <= 0)
{
printf("CLI IS FAILURE\n");
close(fd);
fds[i].fd = -1;
fds[i].events = 0;
continue;
}
printf("fd = %d buff:: %s\n",fd,buff);
send(fd,"OK",2,0);
}
}
}