🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## Epoll工作方式 ***** `LT`水平触发 >LT(level triggered)是epoll缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你 的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表. `ET`边缘触发 >ET (edge-triggered)是高速工作方式,只支持no-block socket,它效率要比LT更高。ET与LT的区别在于,当一个新的事件到来时,ET模式下当然可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字中没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的。而LT模式正好相反,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。 ## 服务端 server.cpp ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/epoll.h> #include <unistd.h> #include <sys/types.h> #define IPADDRESS "127.0.0.1" #define PORT 8787 #define MAXSIZE 1024 #define LISTENQ 5 #define FDSIZE 1000 #define EPOLLEVENTS 100 //函数声明 //创建套接字并进行绑定 static int socket_bind(const char* ip,int port); //IO多路复用epoll static void do_epoll(int listenfd); //事件处理函数 static void handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf); //处理接收到的连接 static void handle_accpet(int epollfd,int listenfd); //读处理 static void do_read(int epollfd,int fd,char *buf); //写处理 static void do_write(int epollfd,int fd,char *buf); //添加事件 static void add_event(int epollfd,int fd,int state); //修改事件 static void modify_event(int epollfd,int fd,int state); //删除事件 static void delete_event(int epollfd,int fd,int state); int main(int argc,char *argv[]) { int listenfd; listenfd = socket_bind(IPADDRESS,PORT); listen(listenfd,LISTENQ); do_epoll(listenfd); return 0; } static int socket_bind(const char* ip,int port) { int listenfd; struct sockaddr_in servaddr; listenfd = socket(AF_INET,SOCK_STREAM,0); if (listenfd == -1) { perror("socket error:"); exit(1); } bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; inet_pton(AF_INET,ip,&servaddr.sin_addr); servaddr.sin_port = htons(port); if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1) { perror("bind error: "); exit(1); } return listenfd; } static void do_epoll(int listenfd) { int epollfd; struct epoll_event events[EPOLLEVENTS]; int ret; char buf[MAXSIZE]; memset(buf,0,MAXSIZE); //创建一个描述符 epollfd = epoll_create(FDSIZE); //添加监听描述符事件 add_event(epollfd,listenfd,EPOLLIN); for ( ; ; ) { //获取已经准备好的描述符事件 ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1); handle_events(epollfd,events,ret,listenfd,buf); } close(epollfd); } static void handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf) { int i; int fd; //进行选好遍历 for (i = 0;i < num;i++) { fd = events[i].data.fd; //根据描述符的类型和事件类型进行处理 if ((fd == listenfd) &&(events[i].events & EPOLLIN)) handle_accpet(epollfd,listenfd); else if (events[i].events & EPOLLIN) do_read(epollfd,fd,buf); else if (events[i].events & EPOLLOUT) do_write(epollfd,fd,buf); } } static void handle_accpet(int epollfd,int listenfd) { int clifd; struct sockaddr_in cliaddr; socklen_t cliaddrlen; clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen); if (clifd == -1) perror("accpet error:"); else { printf("accept a new client: %s:%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port); //添加一个客户描述符和事件 add_event(epollfd,clifd,EPOLLIN); } } static void do_read(int epollfd,int fd,char *buf) { int nread; nread = read(fd,buf,MAXSIZE); if (nread == -1) { perror("read error:"); close(fd); delete_event(epollfd,fd,EPOLLIN); } else if (nread == 0) { fprintf(stderr,"client close.\n"); close(fd); delete_event(epollfd,fd,EPOLLIN); } else { printf("read message is : %s",buf); //修改描述符对应的事件,由读改为写 modify_event(epollfd,fd,EPOLLOUT); } } static void do_write(int epollfd,int fd,char *buf) { int nwrite; nwrite = write(fd,buf,strlen(buf)); if (nwrite == -1) { perror("write error:"); close(fd); delete_event(epollfd,fd,EPOLLOUT); } else modify_event(epollfd,fd,EPOLLIN); memset(buf,0,MAXSIZE); } static void add_event(int epollfd,int fd,int state) { struct epoll_event ev; ev.events = state; ev.data.fd = fd; ev.events = EPOLLIN | EPOLLET; //读入 边缘触发 epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev); } static void delete_event(int epollfd,int fd,int state) { struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev); } static void modify_event(int epollfd,int fd,int state) { struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev); } ``` ## 客户端 ``` #include <netinet/in.h> #include <sys/socket.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/epoll.h> #include <time.h> #include <unistd.h> #include <sys/types.h> #include <arpa/inet.h> #define MAXSIZE 1024 #define IPADDRESS "192.168.0.212" #define SERV_PORT 8787 #define FDSIZE 1024 #define EPOLLEVENTS 20 static void handle_connection(int sockfd); static void handle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf); static void do_read(int epollfd,int fd,int sockfd,char *buf); static void do_write(int epollfd,int fd,int sockfd,char *buf); static void add_event(int epollfd,int fd,int state); static void delete_event(int epollfd,int fd,int state); static void modify_event(int epollfd,int fd,int state); int main(int argc,char *argv[]) { int sockfd; struct sockaddr_in servaddr; sockfd = socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr); connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)); //处理连接 handle_connection(sockfd); close(sockfd); return 0; } static void handle_connection(int sockfd) { int epollfd; struct epoll_event events[EPOLLEVENTS]; char buf[MAXSIZE]; int ret; epollfd = epoll_create(FDSIZE); add_event(epollfd,STDIN_FILENO,EPOLLIN); for ( ; ; ) { ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1); handle_events(epollfd,events,ret,sockfd,buf); } close(epollfd); } static void handle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf) { int fd; int i; for (i = 0;i < num;i++) { fd = events[i].data.fd; if (events[i].events & EPOLLIN) do_read(epollfd,fd,sockfd,buf); else if (events[i].events & EPOLLOUT) do_write(epollfd,fd,sockfd,buf); } } static void do_read(int epollfd,int fd,int sockfd,char *buf) { int nread; nread = read(fd,buf,MAXSIZE); if (nread == -1) { perror("read error:"); close(fd); } else if (nread == 0) { fprintf(stderr,"server close.\n"); close(fd); } else { if (fd == STDIN_FILENO) add_event(epollfd,sockfd,EPOLLOUT); else { delete_event(epollfd,sockfd,EPOLLIN); add_event(epollfd,STDOUT_FILENO,EPOLLOUT); } } } static void do_write(int epollfd,int fd,int sockfd,char *buf) { int nwrite; nwrite = write(fd,buf,strlen(buf)); if (nwrite == -1) { perror("write error:"); close(fd); } else { if (fd == STDOUT_FILENO) delete_event(epollfd,fd,EPOLLOUT); else modify_event(epollfd,fd,EPOLLIN); } memset(buf,0,MAXSIZE); } static void add_event(int epollfd,int fd,int state) { struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev); } static void delete_event(int epollfd,int fd,int state) { struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev); } static void modify_event(int epollfd,int fd,int state) { struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev); } ```