Linux I/O复用——epoll
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux I/O复用——epoll相关的知识,希望对你有一定的参考价值。
1、epoll()
epoll()是Linux特有的I/O复用函数,它的实现与使用上和select()、poll()、有很大差异。
epoll()用一组函数来完成任务,而不是单个函数;其次,epoll()把文件描述放到内核事件表中,只需一个额外的文件描述符,来标识内核中唯一的这个事件表。
需要使用的API:
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
需要使用的结构体信息:
typedef union epoll_data { void *ptr; int fd; //一般情况下,都用的是这个文件描述符 uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ };
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
2、epoll_wait()
关键:对epoll_wait()函数的核心理解
(1)、返回值:事件表中就绪客户端的个数;
(2)、参数events:将事件表中的就绪客户端的信息放到了events数组中。
3、epoll()的核心思想
:就是创建一个内核事件表,存放所监听客户端的套接字和当前的事件,在利用epoll_wait()函数查找就绪的套接字,最后经过增加、删除、修改利用epoll_ctl()函数进行;当然了,这其中还有一批搭配使用的宏;
3、代码实现
(1)、utili.h
#include<unistd.h> #include<stdio.h> #include<string.h> #include<stdlib.h> #include<sys/socket.h> #include<arpa/inet.h> #include<netinet/in.h> #include<assert.h> #include<sys/select.h> #define SERVER_IP "127.0.0.1" #define SERVER_PORT 8787 #define LISTEN_QUEUE 5 #define SIZE 10 #define BUFFER_SIZE 256 #include<poll.h> #define OPEN_MAX 1000 #include<sys/epoll.h> #define FDSIZE 1000 #define EPOLLEVENTS 100
(2)、ser.c
#include"../utili.h" static int socket_bind(const char *ip, int port){ int listenfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addrSer; addrSer.sin_family = AF_INET; //addrSer.sin_addr.s_addr = inet_addr(ip); inet_pton(AF_INET, ip, &addrSer.sin_addr); addrSer.sin_port = htons(port); bind(listenfd, (struct sockaddr*)&addrSer, sizeof(struct sockaddr)); return listenfd; } 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); } static void handle_accept(int epollfd, int listenfd){ int clifd; struct sockaddr_in addrCli; socklen_t len = sizeof(struct sockaddr); clifd = accept(listenfd, (struct sockaddr*)&addrCli, &len); if(clifd != -1){ add_event(epollfd, clifd, EPOLLIN); } } static void do_read(int epollfd, int fd, char *buf){ int nread = read(fd, buf, BUFFER_SIZE); if(nread == -1){ close(fd); delete_event(epollfd, fd, EPOLLIN); }else{ printf("read msg:>%s\n",buf); modify_event(epollfd, fd, EPOLLOUT); } } static void do_write(int epollfd, int fd, char *buf){ int nwrite = write(fd, buf, strlen(buf)+1); if(nwrite == -1){ close(fd); delete_event(epollfd, fd, EPOLLOUT); } else{ modify_event(epollfd, fd , EPOLLIN); } memset(buf, 0, BUFFER_SIZE); } 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_accept(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 do_epoll(int listenfd){ int ret; char buffer[BUFFER_SIZE]; struct epoll_event events[EPOLLEVENTS]; int epollfd = epoll_create(FDSIZE); add_event(epollfd, listenfd, EPOLLIN); for(;;){ //select poll ret = epoll_wait(epollfd, events, EPOLLEVENTS, -1); handle_events(epollfd, events, ret, listenfd, buffer); } close(epollfd); } int main(void){ int listenfd; listenfd = socket_bind(SERVER_IP, SERVER_PORT); listen(listenfd, LISTEN_QUEUE); do_epoll(listenfd); return 0; }
(3)、cli.c
#include"../utili.h" 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); } static void do_read(int epollfd, int fd, int sockfd, char *buf){ int nread = read(fd, buf, BUFFER_SIZE); if(nread == -1) { close(fd); delete_event(epollfd, fd, EPOLLIN); }else{ if(fd == STDIN_FILENO) add_event(epollfd, fd, EPOLLIN); else{ delete_event(epollfd, fd, EPOLLIN); add_event(epollfd, STDOUT_FILENO, EPOLLOUT); } } printf("Ser :>%s", buf); } static void do_write(int epollfd, int fd, int sockfd, char *buf){ int nwrite = write(fd, buf, strlen(buf)+1); if(nwrite == -1){ perror("write"); close(fd); }else{ if(fd == STDOUT_FILENO){ delete_event(epollfd, fd, EPOLLOUT); }else{ modify_event(epollfd, fd, EPOLLIN); } } memset(buf, 0, BUFFER_SIZE); } static void handle_events(int epollfd, struct epoll_event *events, int num, int sockfd, char *buf){ int i; int fd; 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, fd, sockfd, buf) do_write(epollfd, fd, sockfd, buf); } } static void handle_connection(int sockfd){ struct epoll_event events[EPOLLEVENTS]; int epollfd = epoll_create(FDSIZE); add_event(epollfd, STDIN_FILENO, EPOLLIN); int ret; char buffer[BUFFER_SIZE]; for(;;){ ret = epoll_wait(epollfd, events, EPOLLEVENTS, -1); handle_events(epollfd, events, ret, sockfd, buffer); } close(epollfd); } int main(void){ int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addrSer; addrSer.sin_family = AF_INET; addrSer.sin_port = htons(SERVER_PORT); addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); connect(sockfd, (struct sockaddr*)&addrSer, sizeof(struct sockaddr)); handle_connection(sockfd); close(sockfd); return 0; }
运行结果
服务器端就是等待客户端的使用;
客户端1
客户端2
利用epoll()函数,不用轮询每个套接字,效率更高效一些;
本文出自 “11586096” 博客,请务必保留此出处http://11596096.blog.51cto.com/11586096/1856827
以上是关于Linux I/O复用——epoll的主要内容,如果未能解决你的问题,请参考以下文章