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

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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值