名词解释:man epoll之后,得到如下结果:
NAME
SYNOPSIS
DESCRIPTION
其实,一切的解释都是多余的,按照我目前的了解,EPOLL模型似乎只有一种格式,所以大家只要参考我下面的代码,就能够对EPOLL有所了解了,代码的解释都已经在注释中:
while (TRUE)
其实EPOLL的精华,按照我目前的理解,也就是上述的几段短短的代码,看来时代真的不同了,以前如何接受大量用户连接的问题,现在却被如此轻松的搞定,真是让人不得不感叹。
今天搞了一天的epoll,想做一个高并发的代理程序。刚开始真是郁闷,一直搞不通,网上也有几篇介绍epoll的文章。但都不深入,没有将一些注意的地方讲明。以至于走了很多弯路,现将自己的一些理解共享给大家,以少走弯路。
epoll用到的所有函数都是在头文件sys/epoll.h中声明,有什么地方不明白或函数忘记了可以去看一下。
epoll和select相比,最大不同在于:
1epoll返回时已经明确的知道哪个sokcet fd发生了事件,不用再一个个比对。这样就提高了效率。
2select的FD_SETSIZE是有限止的,而epoll是没有限止的只与系统资源有关。
1、epoll_create函数
函数声明:int epoll_create(int size)
该函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个epoll fd上能关注的最大socket fd数。随你定好了。只要你有空间。可参见上面与select之不同2.
22、epoll_ctl函数
函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。
参数:
epfd:由 epoll_create 生成的epoll专用的文件描述符;
op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除
fd:关联的文件描述符;
event:指向epoll_event的指针;
如果调用成功返回0,不成功返回-1
用到的数据结构
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events;
epoll_data_t data;
};
如:
struct epoll_event ev;
//设置与要处理的事件相关的文件描述符
ev.data.fd=listenfd;
//设置要处理的事件类型
ev.events=EPOLLIN|EPOLLET;
//注册epoll事件
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
常用的事件类型:
EPOLLIN :表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:表示对应的文件描述符有事件发生;
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事件发生的超时值(单位我也不太清楚);-1相当于阻塞,0相当于非阻塞。一般用-1即可
返回发生事件数。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXBUF 1024
#define MAXEPOLLSIZE 10000
int setnonblocking(int sockfd)
{
if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)
{
return -1;
}
return 0;
}
int handle_message(int new_fd)
{
char buf[MAXBUF + 1];
int len;
bzero(buf, MAXBUF + 1);
len = recv(new_fd, buf, MAXBUF, 0);
if (len > 0)
{
printf
("%d接收消息成功:'%s',共%d个字节的数据/n",
new_fd, buf, len);
}
else
{
if (len < 0)
printf
("消息接收失败!错误代码是%d,错误信息是'%s'/n",
errno, strerror(errno));
close(new_fd);
return -1;
}
return len;
}
int main(int argc, char **argv)
{
int listener, new_fd, kdpfd, nfds, n, ret, curfds;
socklen_t len;
struct sockaddr_in my_addr, their_addr;
unsigned int myport, lisnum;
struct epoll_event ev;
struct epoll_event events[MAXEPOLLSIZE];
struct rlimit rt;
myport = 5000;
lisnum = 2;
rt.rlim_max = rt.rlim_cur = MAXEPOLLSIZE;
if (setrlimit(RLIMIT_NOFILE, &rt) == -1)
{
perror("setrlimit");
exit(1);
}
else
{
printf("设置系统资源参数成功!/n");
}
if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
else
{
printf("socket 创建成功!/n");
}
setnonblocking(listener);
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = PF_INET;
my_addr.sin_port = htons(myport);
my_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -
1)
{
perror("bind");
exit(1);
}
else
{
printf("IP 地址和端口绑定成功/n");
}
if (listen(listener, lisnum) == -1)
{
perror("listen");
exit(1);
}
else
{
printf("开启服务成功!/n");
}
kdpfd = epoll_create(MAXEPOLLSIZE);
len = sizeof(struct sockaddr_in);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = listener;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0)
{
fprintf(stderr, "epoll set insertion error: fd=%d/n", listener);
return -1;
}
else
{
printf("监听 socket 加入 epoll 成功!/n");
}
curfds = 1;
while (1)
{
nfds = epoll_wait(kdpfd, events, curfds, -1);
if (nfds == -1)
{
perror("epoll_wait");
break;
}
for (n = 0; n < nfds; ++n)
{
if (events[n].data.fd == listener)
{
new_fd = accept(listener, (struct sockaddr *) &their_addr,&len);
if (new_fd < 0)
{
perror("accept");
continue;
}
else
{
printf("有连接来自于: %d:%d, 分配的 socket 为:%d/n",
inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);
}
setnonblocking(new_fd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = new_fd;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, new_fd, &ev) < 0)
{
fprintf(stderr, "把 socket '%d' 加入 epoll 失败!%s/n",
new_fd, strerror(errno));
return -1;
}
curfds++;
}
else
{
ret = handle_message(events[n].data.fd);
if (ret < 1 && errno != 11)
{
epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].data.fd,&ev);
curfds--;
}
}
}
}
close(listener);
return 0;
}
型放入到events数组中。并且将注册在epfd上的socket fd的事件类型给清空,所以如果下一个循环你还要关注
这个socket fd的话,则需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)来重新设置socket fd的事件类
型。这时不用EPOLL_CTL_ADD,因为socket fd并未清空,只是事件类型清空。这一步非常重要。