一、epoll_create
#include <sys/epoll.h> int epoll_create(int size); int epoll_create1(int flags); 返回:成功非负文件描述符,-1出错
size:内核监听数目一共多大
创建一个epoll接口,size参数和select不同,不是fd+1?
需要注意的是:当创建好epoll后,它就会占用一个fd值,在linux /proc/id/fd/能看到这个fd的,所以使用完epoll后,必须close()关闭,否则可能导致耗尽fd。
二、epoll_ctl
#include <sys/epoll.h> int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 返回:0成功,-1失败
epfd:由epoll_create生成的epoll专用的文件描述符
op:要进行的操作,可能取EPOLL_CTL_ADD注册、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL删除
fd:关联的文件描述符
event:指向epoll_event指针
epoll的相关的epoll_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 */ };
其中的events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
三、epoll_wait
#include <sys/epoll.h> int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask);
返回:发生事件数
epfd:由epoll_create生成的epoll专用的文件描述符
epoll_event:用于回传代处理事件的数组
maxevents:每次能处理的事件数
timeout:等待I/O事件发生的超时值
在linux中的man手册中有epoll模型:
1 #define MAX_EVENTS 10 2 struct epoll_event ev, events[MAX_EVENTS]; 3 int listen_sock, conn_sock, nfds, epollfd; 4 5 /* Set up listening socket, 'listen_sock' (socket(), 6 bind(), listen()) */ 7 8 epollfd = epoll_create(10); 9 if (epollfd == -1) { 10 perror("epoll_create"); 11 exit(EXIT_FAILURE); 12 } 13 14 ev.events = EPOLLIN; 15 ev.data.fd = listen_sock; 16 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) { 17 perror("epoll_ctl: listen_sock"); 18 exit(EXIT_FAILURE); 19 } 20 21 for (;;) { 22 nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); //等待IO事件 23 if (nfds == -1) { 24 perror("epoll_pwait"); 25 exit(EXIT_FAILURE); 26 } 27 28 for (n = 0; n < nfds; ++n) { 29 //如果是主socket事件,则表示有新连接进入,需要进行新连接的处理 30 if (events[n].data.fd == listen_sock) { 31 conn_sock = accept(listen_sock, 32 (struct sockaddr *) &local, &addrlen); 33 if (conn_sock == -1) { 34 perror("accept"); 35 exit(EXIT_FAILURE); 36 } 37 //将新连接置于非阻塞模式 38 setnonblocking(conn_sock); 39 ev.events = EPOLLIN | EPOLLET; 40 //注意这里的参数EPOLLIN|EPOLLET并没有设置对写socket的监听 41 //如果有写操作的话,这个时候epoll是不会返回事件的 42 //如果要对写操作也监听的话,应该是EPOLLIN|EPOLLOUT|EPOLLET 43 //并且将新连接也加入EPOLL的监听队列 44 ev.data.fd = conn_sock; 45 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, 46 &ev) == -1) { 47 //加入到epoll的监听队列里,这里用EPOLL_CTL_ADD 48 //来加一个新的epoll事件,可以通过EPOLL_CTL_DEL减少 49 //一个epoll事件,通过EPOLL_CTL_MOD来改变一个i额事件的监听方式 50 perror("epoll_ctl: conn_sock"); 51 exit(EXIT_FAILURE); 52 } 53 } else { 54 //如果不是主socket的事件的话,则代表这是一个用户的socket事件 55 //则用来处理这个用户的socket的事情是,比如说read(fd, xxx)之类,或者一些其他的处理 56 do_use_fd(events[n].data.fd); 57 } 58 } 59 }
下面是个epoll的使用例子:
1 #include <iostream> 2 #include <sys/socket.h> 3 #include <sys/epoll.h> 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <fcntl.h> 7 #include <unistd.h> 8 #include <stdio.h> 9 #include <errno.h> 10 #include <stdlib.h> 11 #include <strings.h> 12 13 using namespace std; 14 15 #define MAXLINE 5 16 #define OPEN_MAX 100 17 #define LISTENQ 20 18 #define SERV_PORT 5000 19 #define INFTIM 1000 20 21 void setnonblocking(int sock) 22 { 23 int opts; 24 opts = fcntl(sock, F_GETFL); 25 if(opts < 0) { 26 perror("fcntl(sock, GETFL)"); 27 exit(1); 28 } 29 opts = opts | O_NONBLOCK; 30 if(fcntl(sock, F_SETFL, opts) < 0) { 31 perror("fcntl(sock, SETFL, opts)"); 32 exit(1); 33 } 34 } 35 36 int main() 37 { 38 int i, maxi, listenfd, connfd, sockfd, epfd, nfds; 39 ssize_t n; 40 char line[MAXLINE]; 41 socklen_t clilen; 42 43 struct epoll_event ev, events[20]; 44 epfd = epoll_create(256); 45 struct sockaddr_in clientaddr; 46 struct sockaddr_in serveraddr; 47 listenfd = socket(AF_INET, SOCK_STREAM, 0); 48 49 setnonblocking(listenfd); 50 ev.data.fd = listenfd; 51 ev.events = EPOLLIN | EPOLLET; 52 epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev); 53 bzero(&serveraddr, sizeof(serveraddr)); 54 serveraddr.sin_family = AF_INET; 55 char *local_addr = "192.168.1.63"; 56 inet_aton(local_addr, &(serveraddr.sin_addr)); 57 serveraddr.sin_port = htons(SERV_PORT); 58 bind(listenfd, (sockaddr *)&serveraddr, sizeof(serveraddr)); 59 listen(listenfd, LISTENQ); 60 61 maxi = 0; 62 for( ; ; ) { 63 nfds = epoll_wait(epfd, events, 20, 500); 64 for(i=0;i<nfds;i++) { 65 if(events[i].data.fd == listenfd) { 66 connfd = accept(listenfd, (sockaddr *)&clientaddr, &clilen); 67 if(connfd < 0) { 68 perror("connfd < 0"); 69 exit(1); 70 } 71 setnonblocking(connfd); 72 char *str = inet_ntoa(clientaddr.sin_addr); 73 std::cout<<"connect from"<<str<<std::endl; 74 ev.data.fd = connfd; 75 ev.events = EPOLLIN | EPOLLET; 76 epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev); 77 } else if(events[i].events & EPOLLIN) { 78 if((sockfd = events[i].data.fd) < 0) { 79 continue; 80 } 81 if((n = read(sockfd, line, MAXLINE)) < 0) { 82 if(errno == ECONNRESET) { 83 close(sockfd); 84 events[i].data.fd = -1; 85 } else { 86 std::cout<<"readline error"<<std::endl; 87 } 88 } else if(n == 0) { 89 close(sockfd); 90 events[i].data.fd = -1; 91 } 92 ev.data.fd = sockfd; 93 ev.events = EPOLLOUT | EPOLLET; 94 epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev); 95 } else if(events[i].events & EPOLLOUT) { 96 sockfd = events[i].data.fd; 97 write(sockfd, line, n); 98 ev.data.fd = sockfd; 99 ev.events = EPOLLIN | EPOLLET; 100 epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev); 101 } 102 } 103 } 104 }