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

来源:互联网 发布:局域网网络扫描仪 编辑:程序博客网 时间:2024/05/17 22:42

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 1024struct 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].fdint 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;}}//ifelse 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}//defaultbreak;}//switch}//whilereturn 0;}


阅读全文
0 0
原创粉丝点击