多路复用之epoll服务器应用
来源:互联网 发布:python xpath解析html 编辑:程序博客网 时间:2024/05/22 17:30
epoll的三个系统调用
int epoll_create(int size); 它会创建一个epoll模型在内存中,创建成功后返回该模型的文件描述符。size表最多能注册多少个文件描述符,在Linux 2.6.8 版本之后不在关心。int epoll_ctl(int epfd,int op,int fd,struct epoll_event*event)epfd :epoll模型的文件描述符。op : 指将那个文件描述符,在红黑树中进行增、删、改的那个操作。fd:指要操作的特定的文件描述符。event:指特定文件描述符所关心的事件。int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);epfd : epoll 模型的文件描述符。events: epoll_event结构体数组的地址。maxevents:数组大小timeout:等待超时的时间设置,单位为毫秒。当设为-1代表阻塞,0代表非阻塞,大于0代表轮询。
op参数EPOLL_CTL_ADDEPOLL_CTL_MODEPOLL_CTL_DELstruct event_event结构体 struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable ,大小为8字节是为了64位下指针为8字节*/ }; typedef union epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t;
具体的模型:
写epoll服务器的步骤
step 1 :
创建出来一个listen_sock
具体为:
socket调用
setsockopt调用(设置后该socket的端口号,可被该主机的其他ip地址绑定,设置后服务器主动断开不再time_wait等待)
fcntl调用(可选项 LT模式可不调用,ET模式必须把该套接字调用设为非阻塞)
bind调用
listen调用step 2:
创建一个epoll模型,epoll_create调用
将listen_sock调用epoll_ctl,把监听套接字加入到红黑树中,并关心listen_sock的读事件。
调用epoll_wait,等待时间就绪
设置 switch case 分支语句step 3:
switch case {
-1:
表调用失败
0:
表超时
default:
表等待成功
for 循环遍历整个传入的event数组,遍历每个fd就绪事件,进行处理
}step 4:
遍历整个数组的逻辑:
if(fd是监听套接字){
accept 调用 (LT这里为if 确保不会被阻塞服务器进程,ET这里为while需遍历完所有数据以防被下次到来的数据覆盖)
将accept 的读事件先注册进红黑树(调用epoll_ctl)
}
else if(其他fd读事件就绪){
进行数据读取,并修改该套接字,将该套接字的写时间也注册进红黑树,一般只要该进程在跑,write事件立刻就绪。
}
else{
这里就是所有文件描述符的写事件了,
这里可以写我们自己想回复的数据
}
LT 模式
LT 模式
只要底层有数据,它会不断通知调用者去取走数据。只要数据没有被取走它会一直把相应事件标志为事件就绪。epoll默认为LT模式,我们可在epoll_ctl中设置为ET模式。
#include<stdio.h>#include<sys/socket.h>#include<sys/types.h>#include <arpa/inet.h>#include <netinet/in.h>#include <sys/epoll.h>#include <stdlib.h>#define SIZE 10240void usege(const char * arg){ printf("%s ip port\n",arg);}typedef struct epoll_buff{int fd; //这个结构体很重要如果没有的话,数据就混乱了读的时候数据全读到buff中了,write的时候这个char buf [SIZE];//buff 不知道到底是对应的是那个文件描述符} epoll_buf,*epoll_buf_p;epoll_buf_p alloc(int fd) { size_t len=sizeof(struct epoll_buff); // printf("size:%d\n",len); epoll_buf_p ret =(epoll_buf_p) malloc(len); if (ret ==NULL) { perror("malloc"); exit(10); } ret->fd=fd; return ret;}int startup(const char * ip,const char * port){ int fd = socket(AF_INET,SOCK_STREAM,0); if(fd<0) { perror("socket"); exit(1); } int opt = 1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); struct sockaddr_in sock_addr; sock_addr.sin_family=AF_INET; sock_addr.sin_port = htons(atoi(port)); sock_addr.sin_addr.s_addr=inet_addr(ip); socklen_t len=sizeof(sock_addr); if((bind(fd,(struct sockaddr*)&sock_addr,len))<0) { perror("bind"); exit(2); } if(listen(fd,128)<0) { perror("listen"); exit(3); } return fd;}int main(int argv,const char * args[]){ if(argv != 3) { usege(args[0]); exit(4); } int listen_sock=startup(args[1],args[2]); int epfd=epoll_create(256); // printf("lsock%d , epfd%d\n",listen_sock,epfd); if(epfd<0) { perror("epoll_create");} struct epoll_event ev; struct epoll_event env[32]; ev.events=EPOLLIN; ev.data.ptr=alloc(listen_sock); if(epoll_ctl(epfd, EPOLL_CTL_ADD,listen_sock,&ev)<0) { perror("epoll_ctl"); exit(5); } while(1) { int timeout = -1; int reve_n = epoll_wait(epfd,env,sizeof(env)/sizeof(env[0]),timeout); // printf("reve_n :%d\n",reve_n); switch (reve_n) { case 0: printf("time out \n"); break; case -1: perror("epoll_wait"); exit(6); default: { int idx=0; for(idx;idx<reve_n;idx++) { epoll_buf_p p=(epoll_buf_p)env[idx].data.ptr; // printf("p->fd %d\n",p->fd); if(p->fd == listen_sock&&env[idx].events==EPOLLIN) { struct sockaddr_in cilent ; socklen_t len=sizeof(cilent); int sock=0; if((sock=accept(listen_sock,(struct sockaddr*)&cilent,&len))>0) // while ET { ev.events=EPOLLIN; ev.data.ptr=alloc(sock); if(epoll_ctl(epfd, EPOLL_CTL_ADD,sock,&ev)<0) { perror("epoll_ctl"); exit(7); } const char * cilent_ip=inet_ntoa(cilent.sin_addr); int cilent_port=ntohs(cilent.sin_port); printf("cilent connect ip:%s port:%u \n",cilent_ip,cilent_port); } if(sock<0) { perror("accept"); } continue; } else if(env[idx].events==EPOLLIN) { int res=read(p->fd,p->buf,SIZE); if(res<0){ perror("read"); exit(8); } else if(res==0) { printf("cilent quit!!\n"); close(p->fd); free(p); epoll_ctl(epfd,EPOLL_CTL_MOD,p->fd,NULL); } else { // if((p->buf)[res-2]=='\r') // (p->buf)[res-2]=0; // else { // (p->buf)[res-1]=0; // } (p->buf)[res]=0; printf("####cilent : %s",p->buf); fflush(stdout); } ev.events=(env[idx]).events|EPOLLOUT; if(epoll_ctl(epfd,EPOLL_CTL_MOD,p->fd,&ev)<0) { perror("epoll_ctl"); exit(9); } } else{ // end read const char * temp ="HTTP/1.1 200 OK\r\n Content-Length :%s \r\n\r\n wo xi huan yutian !!! "; int ret= sprintf(p->buf,"%s",temp); write(p->fd,p->buf,ret); epoll_ctl(epfd,EPOLL_CTL_DEL,p->fd,NULL); close(p->fd); // ev.events=EPOLLIN; // if(epoll_ctl(epfd,EPOLL_CTL_MOD,p->fd,&ev)<0) // { // perror("epoll_ctl"); // exit(9); // } } } } break; }// end switch } // end while(1) close(epfd); return 0;}
ET 模式
ET模式是epoll的高效模式,可以在每次进行epoll_ctl时进行设置为EPOLLET时,该文件描述符以ET模式工作。ET模式只在数据从无到有时通知一次,并且ET模式下工作的套接字为非阻塞套接字。所以要注意当时间就绪时,要一次性把数据都读取完。
#include<stdio.h>#include<sys/socket.h>#include<sys/types.h>#include <arpa/inet.h>#include <netinet/in.h>#include <sys/epoll.h>#include <stdlib.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#define SIZE 4096void usege(const char * arg){ printf("%s ip port\n",arg);}typedef struct epoll_buff{int fd;char buf [SIZE];} epoll_buf,*epoll_buf_p;epoll_buf_p alloc(int fd){ size_t len=sizeof(struct epoll_buff); // printf("size:%d\n",len); epoll_buf_p ret =(epoll_buf_p) malloc(len); if (ret ==NULL) { perror("malloc"); exit(10); } ret->fd=fd; return ret;}static void set_noblock(int fd){ int fd_flag = fcntl(fd,F_GETFL); if(fcntl(fd,F_SETFL,fd_flag|O_NONBLOCK)<0) { perror("fcntl"); exit(11); }}void Read(epoll_buf_p p,int epfd, struct epoll_event * ev_arr){ while((res=read(p->fd,p->buf,SIZE))>0) { (p->buf)[res]=0; printf("#### cilent : %s",p->buf); fflush(stdout); } if(res==0) { printf("cilent quit"); fflush(stdout); if(epoll_ctl(epfd,EPOLL_CTL_DEL,p->fd,NULL)<0) { perror("epoll_ctl"); exit(12); } close(p->fd); } if(res<0&&errno!=EAGAIN) { perror("read"); exit(13); } struct epoll_event ev; ev.events = ev_arr->events|EPOLLOUT; //这步是为了将关心的事件改为即关心读又关心写 ev.data.ptr=p; if(epoll_ctl(epfd,EPOLL_CTL_MOD,p->fd,&ev)<0) { perror("epoll_ctl"); }}void Write(epoll_buf_p p,int epfd){ const char * temp ="HTTP/1.1 200 OK\r\n Content-Length :%s \r\n\r\n wo xihuan yutian !!! "; int ret= sprintf(p->buf,"%s",temp); write(p->fd,p->buf,ret); epoll_ctl(epfd,EPOLL_CTL_DEL,p->fd,NULL); close(p->fd);}int startup(const char * ip,const char * port){ int fd = socket(AF_INET,SOCK_STREAM,0); if(fd<0) { perror("socket"); exit(1); } set_noblock(fd); int opt = 1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); struct sockaddr_in sock_addr; sock_addr.sin_family=AF_INET; sock_addr.sin_port = htons(atoi(port)); sock_addr.sin_addr.s_addr=inet_addr(ip); socklen_t len=sizeof(sock_addr); if((bind(fd,(struct sockaddr*)&sock_addr,len))<0) { perror("bind"); exit(2); } if(listen(fd,128)<0) { perror("listen"); exit(3); } return fd;}int main(int argv,const char * args[]){ if(argv != 3) { usege(args[0]); exit(4); } int listen_sock=startup(args[1],args[2]); int epfd=epoll_create(256); // printf("lsock%d , epfd%d\n",listen_sock,epfd); if(epfd<0) { perror("epoll_create");} struct epoll_event ev; struct epoll_event env[32]; ev.events=EPOLLIN|EPOLLET; ev.data.ptr=alloc(listen_sock); if(epoll_ctl(epfd, EPOLL_CTL_ADD,listen_sock,&ev)<0) { perror("epoll_ctl"); exit(5); } while(1) { int timeout = -1; int reve_n = epoll_wait(epfd,env,sizeof(env)/sizeof(env[0]),timeout); // printf("reve_n :%d\n",reve_n); switch (reve_n) { case 0: printf("time out \n"); break; case -1: perror("epoll_wait"); exit(6); default: { int idx=0; for(idx;idx<reve_n;idx++) { epoll_buf_p p=(epoll_buf_p)env[idx].data.ptr; if(p->fd == listen_sock&&env[idx].events&EPOLLIN) { struct sockaddr_in cilent ; socklen_t len=sizeof(cilent); int sock=0; while((sock=accept(listen_sock,(struct sockaddr*)&cilent,&len))>0) // while ET { set_noblock(sock); ev.events=EPOLLIN|EPOLLET; ev.data.ptr=alloc(sock); if(epoll_ctl(epfd, EPOLL_CTL_ADD,sock,&ev)<0) { perror("epoll_ctl"); exit(7); } const char * cilent_ip=inet_ntoa(cilent.sin_addr); int cilent_port=ntohs(cilent.sin_port); printf("cilent connect ip:%s port:%u \n",cilent_ip,cilent_port); } if(sock<0) { perror("accept"); } continue; } else if(env[idx].events&EPOLLIN) { Read(p,epfd, env[idx]); } else{ Write(p,epfd); } } } break; }// end switch } // end while(1) return 0;}
epoll 服务器优缺点
优点
1 它其中所等待的文件描述符没有上限,具体极限跟硬件设备内存有关。
2 以高效的回调方式通知事件就绪,所以通知事件就绪的时间复杂度为O(1)
3 epoll模型,底层是以红黑树构成,增删查改很高效
4 以回调方式激活就绪节点,很高效。
5 epoll_wait的返回值为就绪事件的个数,所以上层每一次遍历都为有效遍历。
6 使用内存映射技术,将就绪队列映射到event数组上,不需再次进行拷贝数据给上层event数组
缺点
1 代码书写复杂,难以调试。
现象
server
cilent
- 多路复用之epoll服务器应用
- I/O多路复用之epoll服务器
- IO多路复用之epoll
- 多路复用之epoll
- IO多路复用之epoll
- IO多路复用之epoll
- IO多路复用之epoll
- IO多路复用之epoll
- IO多路复用之epoll
- IO多路复用之epoll
- LinuxIO多路复用之epoll
- IO多路复用之epoll
- 多路复用模型之epoll
- 服务器基础:IO多路复用之select、poll、epoll详解
- IO多路复用之epoll总结
- IO多路复用之epoll总结
- I/O多路复用之epoll
- IO多路复用之epoll总结
- iOS AVPlayer播放器 简介
- Centos 7.3 + PHP7.1.7 + Mariadb
- javadoc注释规范
- 代理设计模式
- 链表翻转
- 多路复用之epoll服务器应用
- 公证问答百科
- 【模拟】【贪心】POJ1877Flooded!
- A
- 605. Can Place Flowers
- freeMarker自定义指令
- iOS10.3后允许App运行中变更App图标
- 用<activity-alias/>让配置WXEntryActivity更爽
- Java反射机制之权限压制