您的位置:首页 > 理论基础 > 计算机网络

Linux网络编程【七】:TCP协议高性能服务器(http)模型之I/O多路转接poll

2017-06-12 18:59 435 查看
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息