epoll是Kernel 2.6后新加入的事件机制,在高并发条件下,远优于select.
用个硬件中的例子吧,可能不太恰当:epoll相当于I/O中断(有的时候才相应),而select相当于轮询(总要反复查询)。
其实epoll比slect好用很多,主要一下几个用法。
struct epoll_event ; epoll事件体,事件发生时候你可以得到一个它。其中epoll_event.data.fd可以存储关联的句柄,epoll_event.event 是监听标志,常用的有EPOLLIN (有数据,可以读)、EPOLLOUT(有数据,可以写)EPOLLET(有事件,通用);
(1)创建epoll句柄
int epFd = epoll_create(EPOLL_SIZE);
(2)加入一个句柄到epoll的监听队列
ev.data.fd = serverFd; ev.events = EPOLLIN | EPOLLET; epoll_ctl(epFd, EPOLL_CTL_ADD, serverFd, &ev);
上面的fd是你要绑定给事件发生时候使用的fd,到时候只能操作这个,下面是事件类型。
使用epoll_ctl添加到之中,EPOLL_CTL_ADD是epoll控制类型,这里监听的fd和给event的fd一般相同。
(3)等待event返回
int nfds = epoll_wait(epFd, evs, EVENT_ARR, -1);
传入的evs是epoll_event的数组,EVENT_ARR应当是不超过这个数组的长度。返回nfds的是不超过EVENT_ARR的数值,表示本次等待到了几个事件。
(4)遍历事件
注意,这里遍历的事件是肯定已经发生了的,而select中遍历的是每个fd,而fd不一定在FDSET中(即不一定有读事件发生)!这是效率最大的差别所在!
for (int i = 0; i < nfds; i++)
{
//do something
}
(5)其他技巧
对事件是否是serverFd判断,如果是,进行accept并加入epoll监听队列,要设置异步读取。
如果evts[i]&EPOLLIN,表示可读,使用read进行试探,如果>0表示连接没有关闭,否则连接已经关闭(出发事件又读取不到东西,表示socket关闭!)。如果<0出错。如果>0,需要继续读取直到为0,但是注意这里的为0是在第一次read不为0的前提下,毕竟我们设置了异步读取,暂时没有数据可以读就返回0了!而如果第一次返回0,那么就是关闭吧!
注意关闭要移出出epoll并且close(clientFd)
罗嗦了好多,看代码! 查看源代码 打印帮助 001 /* 002 * main.cc 003 * 004 * Created on: 2009-11-30 005 * Author: liheyuan 006 * Describe: epoll实现阻塞模式服务器(Echo服务器) 007 * 008 * Last Date: 2009-11-30 009 * CopyRight: 2009 @ ICT LiHeyuan 010 */ 011 #include <stdio.h> 012 #include <stdlib.h> 013 #include <unistd.h> 014 #include <fcntl.h> 015 #include <arpa/inet.h> 016 #include <netinet/in.h> 017 #include <sys/epoll.h> 018 #include <errno.h> 019 020 #define EPOLL_SIZE 10 021 #define EVENT_ARR 20 022 #define BACK_QUEUE 10 023 #define PORT 18001 024 #define BUF_SIZE 16 025 026 void setnonblocking(int sockFd) { 027 int opt; 028 029 //获取sock原来的flag 030 opt = fcntl(sockFd, F_GETFL); 031 if (opt < 0) { 032 printf("fcntl(F_GETFL) fail."); 033 exit(-1); 034 } 035 036 //设置新的flag,非阻塞 037 opt |= O_NONBLOCK; 038 if (fcntl(sockFd, F_SETFL, opt) < 0) { 039 printf("fcntl(F_SETFL) fail."); 040 exit(-1); 041 } 042 } 043 044 int main() { 045 046 int serverFd; 047 048 //创建服务器fd 049 serverFd = socket(AF_INET, SOCK_STREAM, 0); 050 setnonblocking(serverFd); 051 052 //创建epoll,并把 serverFd放入监听队列 053 int epFd = epoll_create(EPOLL_SIZE); 054 struct epoll_event ev, evs[EVENT_ARR]; 055 ev.data.fd = serverFd; 056 ev.events = EPOLLIN | EPOLLET; 057 epoll_ctl(epFd, EPOLL_CTL_ADD, serverFd, &ev); 058 059 //绑定服务器端口 060 struct sockaddr_in serverAddr; 061 socklen_t serverLen = sizeof(struct sockaddr_in); 062 serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); 063 serverAddr.sin_port = htons(PORT); 064 if (bind(serverFd, (struct sockaddr *) &serverAddr, serverLen)) { 065 printf("bind() fail.\n"); 066 exit(-1); 067 } 068 069 //打开监听 070 if (listen(serverFd, BACK_QUEUE)) { 071 printf("Listen fail.\n"); 072 exit(-1); 073 } 074 075 //死循环处理 076 int clientFd; 077 sockaddr_in clientAddr; 078 socklen_t clientLen; 079 char buf[BUF_SIZE]; 080 while (1) { 081 //等待epoll事件的到来,最多取EVENT_ARR个事件 082 int nfds = epoll_wait(epFd, evs, EVENT_ARR, -1); 083 //处理事件 084 for (int i = 0; i < nfds; i++) { 085 if (evs[i].data.fd == serverFd && evs[i].data.fd & EPOLLIN) { 086 //如果是serverFd,表明有新连接连入 087 if ((clientFd = accept(serverFd, 088 (struct sockaddr *) &clientAddr, &clientLen)) < 0) { 089 printf("accept fail.\n"); 090 } 091 printf("Connect from %s:%d\n", inet_ntoa(clientAddr.sin_addr), 092 htons(clientAddr.sin_port)); 093 setnonblocking(clientFd); 094 //注册accept()到的连接 095 ev.data.fd = clientFd; 096 ev.events = EPOLLIN | EPOLLET; 097 epoll_ctl(epFd, EPOLL_CTL_ADD, clientFd, &ev); 098 } else if (evs[i].events & EPOLLIN) { 099 //如果不是serverFd,则是client的可读 100 if ((clientFd = evs[i].data.fd) > 0) { 101 //先进行试探性读取 102 int len = read(clientFd, buf, BUF_SIZE); 103 if (len > 0) { 104 //有数据可以读,Echo写入 105 do { 106 if (write(clientFd, buf, len) < 0) { 107 printf("write() fail.\n"); 108 } 109 len = read(clientFd, buf, BUF_SIZE); 110 } while (len > 0); 111 } else if (len == 0) { 112 //出发了EPOLLIN事件,却没有可以读取的,表示断线 113 printf("Client closed at %d\n", clientFd); 114 epoll_ctl(epFd, EPOLL_CTL_DEL, clientFd, &ev); 115 close(clientFd); 116 evs[i].data.fd = -1; 117 break; 118 } else if (len == EAGAIN) { 119 continue; 120 } else { 121 //client读取出错 122 printf("read() fail."); 123 } 124 } 125 } else { 126 printf("other event.\n"); 127 } 128 } 129 } 130 131 return 0; 132 }
|