1 Epoll函数
首先看一下epoll的几个函数的介绍。
1.1 epoll_create函数
1.1.1 函数原型
/**
* @brief 该函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。
*
* @param size size就是你在这个epoll fd上能关注的最大socketfd数
*
* @return 生成的文件描述符
*/
int epoll_create(int size);
1.1.2 函数实例
/* listener为socket文件描述符,kdpfd为epoll文件描述符 */
int listener, kdpfd;
struct sockaddr_in my_addr;
/* 创建socket */
if ((listener =socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
bzero(&my_addr,sizeof(my_addr));
my_addr.sin_family = PF_INET;
my_addr.sin_port = htons(PORT);
my_addr.sin_addr.s_addr =INADDR_ANY;
/* 绑定socket */
if (bind(listener, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
{
perror("bind");
exit(1);
}
/*监听socket */
if (listen(listener, LISNUM)== -1)
{
perror("listen");
exit(1);
}
/* 以上初始化完成后,创建epoll 句柄,
也可以先创建epoll句柄,再初始化socket */
kdpfd =epoll_create(MAXEPOLLSIZE);
1.2 epoll_ctl函数
1.2.1 函数原型
/**
* @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 succ
* -1 fail
*/
int epoll_ctl(int epfd,int op,int fd,structepoll_event*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 */
};
常用的事件类型:
EPOLLIN :表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 表示对应的文件描述符有事件发生;
1.2.2 函数实例
struct epoll_event ev;
//设置要处理的事件类型
ev.events =EPOLLIN | EPOLLET;
//设置与要处理的事件相关的文件描述符
ev.data.fd =listener;
//注册epoll事件,将监听的socket加入epoll
if (epoll_ctl(kdpfd,EPOLL_CTL_ADD, listener, &ev) < 0)
{
fprintf(stderr,"epoll set insertion error: fd=%d\n", listener);
return -1;
}
1.3 epoll_wait函数
1.3.1 函数原型
/**
* @brief 该函数用于轮询I/O事件的发生
*
* @param epfd 由epoll_create 生成的epoll专用的文件描述符
* @param events 用于回传代处理事件的数组
* @param maxevents 每次能处理的事件数
* @param timeout 等待I/O事件发生的超时值;-1相当于阻塞,0相当于非阻塞。一般用-1即可
*
* @return >=0 返回发生事件数
* -1 错误
*/
int epoll_wait(int epfd,structepoll_event* events,int maxevents,int timeout);
1.3.2 函数实例
struct epoll_event events[MAXEPOLLSIZE];
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,&sizeof(struct sockaddr_in));
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);