首先看一下epoll的几个函数的介绍。
1、epoll_create函数
/**
* @brief 该函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存
* 放你想关注的socket fd上是否发生以及发生了什么事件。
* @param size: size就是你在这个epoll fd上能关注的最大socket fd数
* @return 生成的文件描述符*/
int epoll_create(int size);
2、epoll_ctl函数
/**
* @brief 该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删
* 除事件。
* @param epfd : 由 epoll_create 生成的epoll专用的文件描述符
* @param op : 要进行的操作例如注册事件,可能的取值
* EPOLL_CTL_ADD 注册
* EPOLL_CTL_MOD 修改
* EPOLL_CTL_DEL 删除
* @param fd : 关联的文件描述符
* @param event : 指向epoll_event的指针
* @return 0 if success, -1 if fail*/
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
其中用到的数据结构结构如下:
typedef union epoll_data
{void *ptr;intfd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;structepoll_event
{
__uint32_t events;/*Epoll events*/epoll_data_t data;/*User data variable*/};
常用的事件类型:
EPOLLIN :表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 表示对应的文件描述符有事件发生;
例:
structepoll_event ev;//设置与要处理的事件相关的文件描述符
ev.data.fd=listenfd;//设置要处理的事件类型
ev.events=EPOLLIN|EPOLLET;//注册epoll事件
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
3、epoll_wait函数
/**
* @brief 该函数用于轮询I/O事件的发生
* @param epfd : 由epoll_create 生成的epoll专用的文件描述符
* @param events : 用于回传代处理事件的数组
* @param maxevents : 每次能处理的事件数
* @param timeout : 等待I/O事件发生的超时值;-1相当于阻塞,0相当于非阻塞。一般用-1即可
* @return if >=0, 返回发生事件数, if -1, 错误*/
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);
epoll_wait运行的原理:
等侍注册在epfd上的socket fd的事件的发生,如果发生则将发生的sokct fd和事件类型放入到events数组中。并且将注册在epfd上的socket fd的事件类型给清空,所以如果下一个循环你还要关注这个socket fd的话,则需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)来重新设置socket fd的事件类型。这时不用EPOLL_CTL_ADD,因为socket fd并未清空,只是事件类型清空。这一步非常重要。
好了,其实在epoll的使用中无非就用到了上面介绍的几个函数,下面贴一段用epoll实现的服务器代码:
服务器代码
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include
17 #define MAXBUF 1024
18 #define MAXEPOLLSIZE 10000
19 /*
20 setnonblocking - 设置句柄为非阻塞方式21 */
22 int setnonblocking(intsockfd)23 {24 if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)25 {26 return -1;27 }28 return 0;29 }30 /*
31 handle_message - 处理每个 socket 上的消息收发32 */
33 int handle_message(intnew_fd)34 {35 char buf[MAXBUF + 1];36 intlen;37 /*开始处理每个新连接上的数据收发*/
38 bzero(buf, MAXBUF + 1);39 /*接收客户端的消息*/
40 len = recv(new_fd, buf, MAXBUF, 0);41 if (len > 0)42 {43 printf("%d接收消息成功:'%s',共%d个字节的数据\n",44 new_fd, buf, len);45 }46 else
47 {48 if (len < 0)49 printf50 ("消息接收失败!错误代码是%d,错误信息是'%s'\n",51 errno, strerror(errno));52 close(new_fd);53 return -1;54 }55 /*处理每个新连接上的数据收发结束*/
56 returnlen;57 }58 int main(int argc, char **argv)59 {60 intlistener, new_fd, kdpfd, nfds, n, ret, curfds;61 socklen_t len;62 structsockaddr_in my_addr, their_addr;63 unsigned intmyport, lisnum;64 structepoll_event ev;65 structepoll_event events[MAXEPOLLSIZE];66 structrlimit rt;67 myport = 5000;68 lisnum = 2;69 /*设置每个进程允许打开的最大文件数*/
70 rt.rlim_max = rt.rlim_cur =MAXEPOLLSIZE;71 if (setrlimit(RLIMIT_NOFILE, &rt) == -1)72 {73 perror("setrlimit");74 exit(1);75 }76 else
77 {78 printf("设置系统资源参数成功!\n");79 }80 /*开启 socket 监听*/
81 if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)82 {83 perror("socket");84 exit(1);85 }86 else
87 {88 printf("socket 创建成功!\n");89 }90 setnonblocking(listener);91 bzero(&my_addr, sizeof(my_addr));92 my_addr.sin_family =PF_INET;93 my_addr.sin_port =htons(myport);94 my_addr.sin_addr.s_addr =INADDR_ANY;95 if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)96 {97 perror("bind");98 exit(1);99 }100 else
101 {102 printf("IP 地址和端口绑定成功\n");103 }104 if (listen(listener, lisnum) == -1)105 {106 perror("listen");107 exit(1);108 }109 else
110 {111 printf("开启服务成功!\n");112 }113 /*创建 epoll 句柄,把监听 socket 加入到 epoll 集合里*/
114 kdpfd =epoll_create(MAXEPOLLSIZE);115 len = sizeof(structsockaddr_in);116 ev.events = EPOLLIN |EPOLLET;117 ev.data.fd =listener;118 if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0)119 {120 fprintf(stderr, "epoll set insertion error: fd=%d\n", listener);121 return -1;122 }123 else
124 {125 printf("监听 socket 加入 epoll 成功!\n");126 }127 curfds = 1;128 while (1)129 {130 /*等待有事件发生*/
131 nfds = epoll_wait(kdpfd, events, curfds, -1);132 if (nfds == -1)133 {134 perror("epoll_wait");135 break;136 }137 /*处理所有事件*/
138 for (n = 0; n < nfds; ++n)139 {140 if (events[n].data.fd ==listener)141 {142 new_fd = accept(listener, (struct sockaddr *) &their_addr,&len);143 if (new_fd < 0)144 {145 perror("accept");146 continue;147 }148 else
149 {150 printf("有连接来自于: %d:%d, 分配的 socket 为:%d\n",151 inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);152 }153 setnonblocking(new_fd);154 ev.events = EPOLLIN |EPOLLET;155 ev.data.fd =new_fd;156 if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, new_fd, &ev) < 0)157 {158 fprintf(stderr, "把 socket '%d' 加入 epoll 失败!%s\n",159 new_fd, strerror(errno));160 return -1;161 }162 curfds++;163 }164 else
165 {166 ret =handle_message(events[n].data.fd);167 if (ret < 1 && errno != 11)168 {169 epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].data.fd,&ev);170 curfds--;171 }172 }173 }174 }175 close(listener);176 return 0;177 }
epoll用到的所有函数都是在头文件sys/epoll.h中声明,有什么地方不明白或函数忘记了可以去看一下。
epoll和select相比,最大不同在于:
epoll返回时已经明确的知道哪个socket fd发生了事件,不用再一个个比对。这样就提高了效率。在select 中,要去判断每个socket, 通过if(FD_ISSET(socket, &集合));在 epoll 中,for(inti=0; i
select的FD_SETSIZE是有限止的,而epoll是没有限止的只与系统资源有关。
注:本文选自http://www.cnblogs.com/cipc/articles/2431042.html,
如有侵犯您的权益,请邮件通知我,我将会在收到通知后尽快删除相关内容。