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的指针实现
![](http://img.blog.csdn.net/20170612184026457?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU0MDk5NTU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它
![](http://img.blog.csdn.net/20170612184043472?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU0MDk5NTU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
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设置:
![](http://img.blog.csdn.net/20170612184721434?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU0MDk5NTU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
poll优缺点:
优点:改善了select中连接数受限,不再受限
缺点:与select的后两点类似,poll仍然需要将pollfd数组拷贝到内核空间,之后依次扫描fd的状态,整体复杂度依然是O(n)的,在并发量大的情况下服务器性能会快速下降。
测试结果:
![](http://img.blog.csdn.net/20170612190030187?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU0MDk5NTU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](http://img.blog.csdn.net/20170612190042281?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU0MDk5NTU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
服务器具体实现代码:
不同与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; }
相关文章推荐
- Linux网络编程【五】:TCP协议高性能服务器(http)模型之I/O多路转接select
- Linux网络编程【六】:TCP协议高性能服务器(http)模型之I/O多路转接epoll
- Linux高性能服务器之多路转接(2)---poll模型
- 高性能服务器——I/O多路转接的三种模式(select &poll& epoll)
- linux网络编程----->高并发--->poll多路I/O转接服务器
- Linux高性能服务器之多路转接(1)----select模型实现
- (五十四)高并发服务器——多路IO转接机制poll模型
- 【Linux网络编程】基于TCP协议 I/O多路转接(select) 的高性能回显服务器客户端模型
- Linux网络编程--9. 服务器模型 非常好 (http://www.fanqiang.com)
- Linux网络编程——tcp并发服务器(poll实现)
- 【Linux网络编程】基于TCP流 I/O多路转接(poll) 的高性能http服务器
- 多路转接模型之poll
- TCP协议下的客户机和服务器模型
- Linux【网络编程】——I/O多路转接之Poll服务器
- I/O多路转接之poll服务器
- uc笔记10---网络通信,套接字(Socket),基于 TCP 协议的客户机/服务器模型
- (五十三)高并发服务器——多路IO转接机制Select模型
- 网络七层协议 五层模型 TCP连接 HTTP连接 socket套接字
- Linux网络编程HTTP协议的目录浏览和文件下载服务器
- C/S通信---服务器IO多路复用模型之poll的使用