epoll用到的所有函数都是在头文件sys/epoll.h中声明的,下面简要说明所用到的数据结构和函数:
所用到的数据结构
1 2 3 4 5 6 7 8 9 10 11 |
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 */ }; |
结构体epoll_event 被用于注册所感兴趣的事件和回传所发生待处理的事件,其中epoll_data 联合体用来保存触发事件的某个文件描述符相关的数据,例如一个client连接到服务器,服务器通过调用accept函数可以得到于这个client对应的socket文件描述符,可以把这文件描述符赋给epoll_data的fd字段以便后面的读写操作在这个文件描述符上进行。epoll_event 结构体的events字段是表示感兴趣的事件和被触发的事件可能的取值为:
1 2 3 4 5 6 |
EPOLLIN :表示对应的文件描述符可以读; EPOLLOUT:表示对应的文件描述符可以写; EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来); EPOLLERR:表示对应的文件描述符发生错误; EPOLLHUP:表示对应的文件描述符被挂断; EPOLLET:表示对应的文件描述符有事件发生; |
所用到的函数:
1、epoll_create函数
函数声明:int epoll_create(int size)
该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围(我觉得这个参数和select函数的第一个参数应该是类似的但是该怎么设置才好,我也不太清楚)。
2、epoll_ctl函数
函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。
参数:epfd:由 epoll_create 生成的epoll专用的文件描述符;
op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修
改、EPOLL_CTL_DEL 删除
fd:关联的文件描述符;
event:指向epoll_event的指针;
如果调用成功返回0,不成功返回-1
3、epoll_wait函数
函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
该函数用于轮询I/O事件的发生;
参数:
epfd:由epoll_create 生成的epoll专用的文件描述符;
epoll_event:用于回传代处理事件的数组;
maxevents:每次能处理的事件数;
timeout:等待I/O事件发生的超时值;
返回发生事件数。
以代码来说明问题:
首先给出server的代码,需要说明的是每次accept的连接,加入可读集的时候采用的都是LT模式,而且接收缓冲区是5字节的,也就是每次只接收5字节的数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
#include<iostream> #include<sys/socket.h> #include<sys/epoll.h> #include<netinet/in.h> #include<arpa/inet.h> #include<fcntl.h> #include<unistd.h> #include<stdio.h> #include<errno.h> using namespace std; #define MAXLINE 5 #define OPEN_MAX 100 #define LISTENQ 20 #define SERV_PORT 5000 #define INFTIM 1000 void setnonblocking(int sock) { int opts; opts = fcntl(sock,F_GETFL); if(opts < 0) { perror(" fcntl(sock,GETFL)"); exit (1); } opts = opts|O_NONBLOCK; if(fcntl(sock,F_SETFL,opts) < 0) { perror("fcntl(sock,SETFL,opts)"); exit (1); } } int main() { int i, maxi, listenfd, connfd, sockfd, epfd, nfds; ssize_t n; char line[MAXLINE]; socklen_t clilen; // 声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件 struct epoll_event ev,events[20]; // 生成用于处理accept的epoll专用的文件描述符 epfd = epoll_create(256); struct sockaddr_in clientaddr; struct sockaddr_in serveraddr; listenfd = socket(AF_INET, SOCK_STREAM, 0 ); // 把socket设置为非阻塞方式 setnonblocking(listenfd); // 设置与要处理的事件相关的文件描述符 ev.data.fd = listenfd; // 设置要处理的事件类型 ev.events = EPOLLIN; // 注册epoll事件 epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; char *local_addr = "127.0.0.1"; inet_aton(local_addr, &(serveraddr.sin_addr)); // htons(SERV_PORT); serveraddr.sin_port = htons(SERV_PORT); bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr)); listen(listenfd, LISTENQ); maxi = 0; for(;;) { // 等待epoll事件的发生 nfds = epoll_wait(epfd, events, 20 , 500); // 处理所发生的所有事件 for(i = 0 ;i < nfds; ++ i) { if(events[i].data.fd == listenfd) { clilen=sizeof(struct sockaddr); connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clilen); if (connfd < 0 ) { perror("connfd<0"); exit ( 1 ); } // setnonblocking(connfd); char *str = inet_ntoa(clientaddr.sin_addr); cout << " accapt a connection from " << str << endl; // 设置用于读操作的文件描述符 ev.data.fd = connfd; // 设置用于注测的读操作事件 ev.events = EPOLLIN|EPOLLET; // ev.events = EPOLLIN; // 注册ev epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev); } else if(events[i].events & EPOLLIN) { cout << " EPOLLIN " << endl; if((sockfd = events[i].data.fd) < 0) continue; if((n = read(sockfd, line, MAXLINE)) < 0) { if(errno == ECONNRESET) { close(sockfd); events[i].data.fd = - 1 ; } else std::cout << " readline error " << std::endl; } else if (n == 0 ) { close(sockfd); events[i].data.fd = - 1 ; } line[n] = ' \0'; cout<<" read "<<line<<endl; // 设置用于写操作的文件描述符 ev.data.fd = sockfd; // 设置用于注测的写操作事件 ev.events = EPOLLOUT|EPOLLET; // 修改sockfd上要处理的事件为EPOLLOUT // epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd, & ev); } else if (events[i].events & EPOLLOUT) { sockfd = events[i].data.fd; write(sockfd, line, n); // 设置用于读操作的文件描述符 ev.data.fd = sockfd; // 设置用于注测的读操作事件 ev.events = EPOLLIN|EPOLLET; // 修改sockfd上要处理的事件为EPOLIN epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd, & ev); } } } return 0 ; } |
发表评论
要发表评论,您必须先登录。