I/O多路复用之epoll服务器
来源:互联网 发布:申万宏源交易软件下载 编辑:程序博客网 时间:2024/06/15 18:18
1、epoll服务器的函数
epoll是linux特有的I/O复用函数,它的实现和select和poll有很大的差异。epoll是使用一组函数来完成任务,而不是单个的函数。epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中,而无需向select和poll那样每次调用都要重复传入文件描述符集或事件集,但是epoll却需要一个额外的文件描述符,来唯一标识内核中的这个事件表。
1、epoll_create函数:
这个函数是用来创建epoll事件的模型,它只有一个参数来表示创建的这个事件表是需要多大的。函数返回的文件描述符作为epoll其他函数的第一个参数,用来指定要访问的内核时间表。
2、epoll_ctl函数:
这个函数有四个参数,epfd是epoll_create这个函数的返回值,fd是要操作的文件描述符,op参数是指定的操作类型,而操作类型有以下三种:
EPOLL_CTL_ADD:往事件表上注册fd上的事件
EPOLL_CTL_MOD:修改fd上的注册事件
EPOLL_CTL_DEL:删除fd上的注册事件
event参数指定事件,它是epoll_event结构指针类型。epoll_event的定义如下:
events成员描述事件类型,而epoll_event中的data成员用于存储用户数据,而其中epoll_data_t定义如上图所示。
epoll_data_t是一个联合体,其中用的最多的是fd,它用来指定事件所从属的目标文件描述符。ptr成员可用来指定与fd相关的用户数据。但是epoll_data_t是一个联合体我们不能同时使用ptr成员和fd成员。
epoll_ctl成功的时候返回的是0,失败就会返回-1,并设置errno。
3、epoll_wait函数:
第一个参数epfd还是epoll_create函数的返回值,maxevents表示的是最多监听的 事件,它是必须大于0的。
timeout是要等待的时间,它的含义与poll函数中的timeout的含义是相同的。
epoll_wait这个函数如果检测到事件,就将所有继续的事件从内核事件表复制到它的第二个参数events的数组中去。
2、ET模式和LT模式
epoll对文件描述符的操作有两种模式:LT模式(水平触发)和ET模式(边沿触发),LT是默认的工作模式,在这种模式下epoll相当于一个效率较高的epoll,当往epoll内核事件表中注册一个文件描述符上的EPOLLET事件,epoll将以ET模式来操作该文件描述符,ET模式是epoll的高效工作方式。
ET模式在很大程度上降低了同一个epoll事件的被重复触发的次数,因此它的效率要比LT模式要高。
3、三种服务器的比较
4、epoll服务器的优点
1、底层采用回调机制来激活节点,将已经就绪的文件描述符加到就绪队列中去。
2、当有了新的事件就绪的时候这个事件就会被加到epoll事件的红黑树中去,红黑树的增删查改的时间复杂度(O (N*lgN))是要比数组高很多
3、epoll_wait获得就绪的文件描述符是从就绪队列中获取的,它的时间复杂度为O (1)这是epoll的时间复杂度也是epoll高效的原因。
4、epoll关心的文件描述符是没有上限的
5、就绪队列中的节点会映射到epoll_wait中的events结构体中,节省了一次内核态到用户态的数据拷贝,这是用的mmap技术(内存映射)。
6、就绪事件的陈列的方式是不同的,epoll访问的是就绪队列避免了访问没价值的数据,而select是一个数组保存的。
5、代码实现
epoll.c:
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/socket.h>#include<sys/epoll.h>#include<sys/types.h>#include<fcntl.h>#include<arpa/inet.h>#include<netinet/in.h>#include<errno.h>#define MAX_EVENTS 10#define SIZE 64typedef struct epbuff{ int fd; char buf[2048]; int index;}epbuff_t,*epbuff_p,**epbuff_pp;static epbuff_p alloc_buff(int fd) //此处开空间之后后面就要是ptr指向的一段空间{ epbuff_p ptr = (epbuff_p)malloc(sizeof(epbuff_t)); if(NULL == ptr) { perror("malloc"); exit(9); } ptr->fd = fd; return ptr;}static void dealloc(epbuff_p ptr)//既然要malloc就得去free{ if(NULL != ptr) { free(ptr); ptr = NULL; }}static usage(const char* proc){ printf("Usage: [local_ip],[local_port]%s\n",proc);}int setnonblocking(int sock) //将文件描述符设置为非阻塞的{ int old_option = fcntl(sock,F_GETFL); int new_option = old_option | O_NONBLOCK; fcntl(sock,F_SETFL,new_option); return old_option;}int startup(const char* ip,int port){ int sock = socket(AF_INET,SOCK_STREAM,0); if(sock < 0) { perror("socket"); exit(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"); exit(3); } if(listen(sock,10) < 0) { perror("listen"); exit(4); } return sock;}//实现的是ET版本的epoll服务器 要实现非阻塞版本int myread(int sock,char* buf){ int len = 0; int total = 0; while((len = read(sock,buf+total,1024) > 0) && (len = 1024 )) { total += len; } if(len > 0 && len< 1024) { total += len; } buf[total] = '\0'; return total;}int main(int argc,char* argv[]){ if(argc != 3) { usage(argv[0]); return 1; } int listen_sock = startup(argv[1],atoi(argv[2])); int epfd = epoll_create(SIZE);//先创建一个红黑树和队列 if(epfd < 0) { perror("epoll_create"); exit(5); } struct epoll_event ev; ev.events = EPOLLIN | EPOLLET; ev.data.ptr = alloc_buff(listen_sock); setnonblocking(listen_sock);//由于是ET工作模式下 将listen_sock设置为非阻塞的状态 if(epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&ev) < 0)//往事件表上注册fd事件(把listen_sock)加到红黑树上去 { perror("epoll_ctl"); exit(6); } int timeout = 5000; int nums = -1; struct epoll_event revs[MAX_EVENTS]; while(1) { nums = epoll_wait(epfd,revs,MAX_EVENTS,timeout); switch(nums) { case 0: printf("time out!!!...\n"); break; case -1: perror("epoll_wait"); break; default: { int i = 0; for(;i < nums ;i++) { int sock = ((epbuff_p)(revs[i].data.ptr))->fd; if(sock == listen_sock && revs[i].events == EPOLLIN) { struct sockaddr_in client; socklen_t len = sizeof(client); int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len); if(new_sock < 0) { perror("myaccept"); continue; } printf("get a new client! ip: %s port:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port)); ev.events = EPOLLIN | EPOLLOUT; setnonblocking(new_sock); ev.data.ptr = alloc_buff(new_sock); int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,new_sock,&ev); if(ret < 0) { perror("epoll_ctl"); exit(7); } } else if(sock != listen_sock && (revs[i].events & EPOLLIN)) { //char buf[1024]; char* buf = ((epbuff_p)(revs[i].data.ptr))->buf; ssize_t s = myread(sock,buf); if( s > 0) { buf[s] = '\0'; printf("client#:%s\n",buf); //如果读成功之后就会直接关心其写操作 ev.events=EPOLLOUT; epoll_ctl(epfd,EPOLL_CTL_MOD,sock,&ev); } else if(s == 0) // 直接关闭连接 { printf("client is close!\n"); dealloc(revs[i].data.ptr); epoll_ctl(epfd,EPOLL_CTL_DEL,sock,NULL); close(sock); } else { perror("read"); dealloc(revs[i].data.ptr); int ret = epoll_ctl(epfd,EPOLL_CTL_DEL,sock,NULL); if(ret < 0) { perror("epoll_ctl"); exit(8); } close(sock); } } else if(sock != listen_sock && (revs[i].events &EPOLLOUT)) { const char* msg = "HTTP/1.0 200 OK\r\n\r\n Hello epoll! "; //const char* msg = "Hello epoll!\n"; ssize_t s = write(sock,msg,strlen(msg)); if(s < 0) { perror("write"); exit(10); } dealloc(revs[i].data.ptr); epoll_ctl(epfd,EPOLL_CTL_DEL,sock,NULL); close(sock); } } break; } } } return 0;}
6、运行结果
服务器端:
客户端:
- I/O多路复用之epoll服务器
- I/O多路复用之epoll
- I/O多路复用之-epoll
- I/O多路复用之epoll
- I/O多路复用之epoll
- I/O多路复用之epoll
- I/O多路复用—epoll服务器
- epoll I/O 多路复用
- 多路复用I/O--epoll
- I/O多路复用技术之 - epoll
- I/O 多路复用之 Select & Epoll
- I/O多路复用之select/poll/epoll
- I/O多路复用之 epoll 系统调用
- Linux--高级I/O多路复用之epoll
- I/O多路复用之epoll学习总结
- Epoll编程-I/O多路复用
- epoll实现I/O多路复用
- epoll编程-I/O多路复用
- python queue
- codeforces 830B. Cards Sorting
- POJ 3135 Polygons on the Grid 笔记
- HDU 1029 Ignatius and the Princess IV (思路)
- 20header和footer
- I/O多路复用之epoll服务器
- php 正则验证手机和邮箱
- Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals)
- 遇到tableView卡顿嘛?会造成卡顿的原因大致有哪些?TableView的性能优化
- java web绝对路径的使用
- dijkstra的优先队列优化
- Java Swing实现高仿电脑版微信
- hibernate里面的一对多关系映射
- Qt5基础(三)Qt登录对话框学习笔记