1.epoll功能描述
1.1 将检测文件描述符的变化委托给内核去处理, 然后内核将发生变化的文件描述符对应的事件返回给应用程序。
2.eopll相关函数分析
2.1 int epoll_create(int size)
2.1.1 函数说明: 创建一个树根
2.1.2 参数说明:
size: 最大节点数, 此参数在linux 2.6.8已被忽略, 但必须传递一个大于0的数。
2.1.3 返回值:
成功: 返回一个大于0的文件描述符, 代表整个树的树根。
失败: 返回-1, 并设置errno值。
2.2 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
2.2.1 函数说明: 将要监听的节点在epoll树上添加, 删除和修改。
2.2.2 参数说明:
epfd: epoll树根
op:
EPOLL_CTL_ADD: 添加事件节点到树上
EPOLL_CTL_DEL: 从树上删除事件节点
EPOLL_CTL_MOD: 修改树上对应的事件节点
fd: 事件节点对应的文件描述符
event: 要操作的事件节点
struct epoll_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 监听的事件类型 */
epoll_data_t data; /* 存放用户的相关变量信息 */
};
events常用的有:
EPOLLIN: 读事件
EPOLLOUT: 写事件
EPOLLERR: 错误事件
EPOLLET: 边缘触发模式
fd: 要监控的事件对应的文件描述符
2.2.3 返回值:
成功: 返回0
失败: 返回-1, 并设置errno值
2.3 int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
2.3.1 函数说明:等待内核返回事件发生
2.3.2 参数说明:
epfd: epoll树根
events: 传出参数, 其实是一个事件结构体数组
maxevents: 数组大小
timeout: 单位:毫秒ms
-1: 表示永久阻塞
0: 立即返回
>0: 表示超时等待事件
2.3.3 返回值:
成功: 返回发生事件的个数。
失败: 若timeout=0, 没有事件发生则返回; 返回-1, 设置errno值。
3. epoll的两种工作模式
3.1 epoll的两种模式ET和LT模式
3.1.1 水平触发(LT): 高电平代表1
只要缓冲区中有数据, 就一直通知。
3.1.2 边缘触发(ET): 电平有变化就代表1
缓冲区中有数据只会通知一次, 之后再有数据才会通知(若是读数据的时候没有读完, 则剩余的数据不会再通知, 直到有新的数据到来)。
3.2 epoll默认工作模式
epoll默认工作模式是LT模式。
4.epoll优化
4.1 ET模式由于只通知一次, 所以在读的时候要循环读, 直到读完, 但是当读完之后read就会阻塞, 所以应该将该文件描述符设置为非阻塞模式。
4.2 边缘非阻塞模式: 提高效率。
5.epoll使用源码
5.1 源码功能介绍:epoll服务器等待客户端的连接到来,接收客户端发过来的信息并回复。
5.2 源码展示
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <sys/epoll.h>
#include <fcntl.h>
int main()
{
int n;
int i;
int nready;
int lfd;
int cfd;
int sockfd;
char buf[1024];
socklen_t socklen;
struct sockaddr_in svraddr;
struct epoll_event ev;
struct epoll_event events[1024];
lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd <=0)
{
return -1;
}
int opt = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
svraddr.sin_family = AF_INET;
svraddr.sin_addr.s_addr = htonl(INADDR_ANY);
svraddr.sin_port = htons(8888);
if(bind(lfd, (struct sockaddr *)&svraddr, sizeof(struct sockaddr_in)) < 0)
{
close(lfd);
return -1;
}
if(listen(lfd, 128) < 0)
{
close(lfd);
return -1;
}
int epfd = epoll_create(1024);
if(epfd<0)
{
perror("epoll_create error");
close(lfd);
return -1;
}
ev.data.fd = lfd;
ev.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
while(1)
{
nready = epoll_wait(epfd, events, 1024, -1);
if(nready<0)
{
perror("epoll_wait error");
if(errno==EINTR)
{
continue;
}
exit(1);
}
printf("nready==[%d]\n", nready);
for(i=0; i<nready; i++)
{
sockfd = events[i].data.fd;
if(sockfd==lfd)
{
cfd = accept(sockfd, NULL, NULL);
ev.data.fd = cfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);
int flags = fcntl(cfd, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(cfd, F_SETFL, flags);
continue;
}
while(1)
{
memset(buf, 0x00, sizeof(buf));
n = read(sockfd, buf, 3);
if(n==-1)
{
if(errno==EAGAIN)
{
printf("read over, n==[%d]\n", n);
break;
}
else
{
printf("read error, n==[%d]\n", n);
close(sockfd);
epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
break;
}
}
else if(n==0)
{
printf("client closed, n==[%d]\n", n);
close(sockfd);
epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
break;
}
else if(n>0)
{
printf("read over, n==[%d],buf==[%s]\n", n, buf);
write(sockfd, buf, n);
}
}
}
}
close(epfd);
close(lfd);
return 0;
}