poll:poll的实现和select非常相似,只是描述fd集合的方式不同,poll使用pollfd结构而不是select的fd_set结构,其他的都差不多。
不同与select使⽤三个位图来表⽰三个fdset的⽅式,poll使⽤⼀个 pollfd的指针实现
该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它
pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的⽅式。同时,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。 和select函数⼀样,poll返回后,需要轮询pollfd来获取就绪的描述符。从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在⼀时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。
参数说明:
fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符;
每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况;
nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;
timeout:是poll函数阻塞的时间,单位:毫秒;
如果timeout==0,那么poll() 函数立即返回而不阻塞
如果timeout==INFTIM,即负数,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;
poll()函数会以轮询方式在timeout所指定的毫秒时间长度之后返回
返回值:
>0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;
==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒
-1: poll函数调用失败,同时会自动设置全局变量errno;
其中结构体struct pollfd 中 参数 events设置:
poll优缺点:
优点:改善了select中连接数受限,不再受限
缺点:与select的后两点类似,poll仍然需要将pollfd数组拷贝到内核空间,之后依次扫描fd的状态,整体复杂度依然是O(n)的,在并发量大的情况下服务器性能会快速下降。
测试结果:
服务器具体实现代码:
/*************************************************************************
> File Name: my_poll.c
> Author: liumin
> Mail: 1106863227@qq.com
> Created Time: Mon 12 Jun 2017 03:12:01 PM CST
************************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/stat.h>
#include<poll.h>
#define _SIZE 1024
struct pollfd arr_pollfd[_SIZE];
static void Usage(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");
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)
{
Usage(argv[0]);
return 1;
}
int listen_sock = startup(argv[1], atoi(argv[2]));
arr_pollfd[0].fd = listen_sock;//将负责监听连接的fd注册事件
arr_pollfd[0].events = POLLIN;
int i = 1;
//初始化数组中的描述符
for(;i < _SIZE ;i++)
{
arr_pollfd[i].fd = -1;
}
int timeout = -1;//设置超时时间
int nums = 0;
while(1)
{
switch(nums = poll(arr_pollfd, _SIZE, timeout))
{
case -1:
perror("poll");
break;
case 0:
printf("timeout...\n");
break;
default:
{
int k = 0;
for(; k < _SIZE;k++)
{
char buf[10240];
if(arr_pollfd[k].fd == listen_sock && arr_pollfd[k].revents & POLLIN)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
//接受返回连接 listen_sock == arr_pollfd[0].fd
int new_sock = accept(arr_pollfd[k].fd, (struct sockaddr*)&client, &len);
if(new_sock < 0)
{
perror("accept");
continue;
}
printf("get a new connect : ip %s , port %d \n",\
inet_ntoa(client.sin_addr),ntohs(client.sin_port));
//将新的描述符(new_sock)添加进数组中
int j = 1;
for(; j < _SIZE;j++)
{
if(arr_pollfd[j].fd < 0)
{
arr_pollfd[j].fd = new_sock;
arr_pollfd[j].events = POLLIN;
break;
}
}
if(j == _SIZE)
{
printf("超出连接数!!!\n");
close(new_sock);
return 5;
}
}//if
else if(arr_pollfd[k].fd > 0)
{
//连接事件就绪
if(arr_pollfd[k].revents & POLLIN)
{
//char buf[1024];
ssize_t s = read(arr_pollfd[k].fd, buf, sizeof(buf)-1);
if(s > 0)
{
buf[s] = 0;
printf("client say # %s\n",buf);
arr_pollfd[k].events = POLLOUT;
}
else
{
printf("client quit!\n");
close(arr_pollfd[k].fd);
arr_pollfd[k].fd = -1;
}
}
else if(arr_pollfd[k].revents & POLLOUT)
{
//写事件就绪
const char* msg = "HTTP/1.1 200 OK\r\n\r\n<html><h1>Hello Poll!</h1></html>";
write(arr_pollfd[k].fd, msg, strlen(msg));
close(arr_pollfd[k].fd);
arr_pollfd[k].fd = -1;
}
}//else if
}//for
}//default
break;
}//switch
}//while
return 0;
}