Epoll
接口
1.创建文件句柄
int epoll_create(int size);
2.向epoll对象修改.添加.删除事件.
int epoll_ctl(int epfd,int op,int fd,struct epoll_event*event);
events取值:
EPOLLIN 表示数据可以读出(接受.关闭连接)
EPOLLOUT 表示连接可以写入数据发送(向服务器发起连接.连接成功事件)
EPOLLERR 表示对应连接发送错误
EPOLLHUP 表示对应的连接被挂起
op取值:
EPOLL_CTL_ADD 添加新的事件到epoll中
EPOLL_CTL_MOD 修改EPOLL中的事件
EPOLL_CTL_DEL 删除epoll中的事件
3.收集在epoll监控中的事件已经发生的事件
int epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout);
epfd:epoll的描述符.为epoll_create函数创建.
events:则是分配好的epoll_event结构体数组.epoll将会把发生的事件复制到events数组中(events不可以是空指针.内核只负责把数据复制到这个events数组中.不会去帮助我们在用户态中分配内存.内核这种做法效率很高).
maxevents:本次可以返回的最大事件数目.通常maxevents参数与预分配的events数组的大小是相等的.
timeout:表示在没有检测到事件发生时最多等待的时间(单位为毫秒).如果timeout为0.立刻返回.不会等待.-1表示无限期阻塞.
epoll关键结构
struct epoll_event{
_uint32_t events;
epoll_data_t data;
}
typedef union epoll_data{
void*ptr;
int fd; //描述符
uint32_t u32;
uint64_t u64;
}
边缘触发与水平触发
Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时.epoll_wait()会通知处理程序去读写.如果这次没有把数据一次性全部读写完(如读写缓冲区太小).那么下次调用 epoll_wait()时.它还会通知你在上没读写完的文件描述符上继续读写.当然如果你一直不去读写.它会一直通知你.如果系统中有大量你不需要读写的就绪文件描述符.而它们每次都会返回.这样会大大降低处理程序检索自己关心的就绪文件描述符的效率.
水平触发需要做更多的事情
Edge_triggered(边缘触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!
设置方式: stat->_ev.events = EPOLLIN | EPOLLET
设置事件的时候加上|EPOLLET
Epoll_demo
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <event.h>
#define SERVER_PORT 9000
#define SERVER_IP "127.0.0.1"
#define MAX_FD 10
extern struct epoll_event ev;
struct epoll_event ev;
int create_sockfd();
void epoll_add(int epfd,int fd);
void epoll_del(int epfd,int fd);
void epoll_mod(int epfd,int fd);
int main(void){
char buff[256];
int sockfd = create_sockfd();
int epfd = epoll_create(MAX_FD);
//设置结构体成员
ev.events=EPOLLIN;
ev.data.fd =sockfd;
epoll_add(epfd,sockfd);
struct epoll_event events[MAX_FD];//接收请求事件
printf("等待客户端连接!\n");
while (1)
{
int nread;
/* printf("server waiting\n");*/
int n = epoll_wait(epfd,events,MAX_FD,5000);//设置的超时时间为五秒
if(n==0){
//表示没有事件发生
printf("time out\n");
} else if(n==-1){
//出错
perror("epoll_wait error!\n");
} else{
int i=0;
for(;i<n;i++){
int fd =events[i].data.fd;
if(fd==-1){
continue;//如果失败则跳过当前
}
if(fd==sockfd){
//如果是服务器描述符.则表示客户端请求连接
struct sockaddr_in client_addr;
memset(&client_addr,'\0',sizeof(client_addr));
socklen_t len = sizeof(client_addr);
int client_sock=accept(sockfd,(struct sockaddr*)&client_addr,&len);
if(client_sock<0){
continue;
}
printf("client fd:%d.\n",client_sock);
epoll_add(epfd,client_sock);//client_sock添加到内核事件表中
}
else {
if(events[i].events&EPOLLIN){
//如果为已经就绪的事件且为epollin
nread=read(fd,buff,sizeof(buff)-1);
buff[nread]='\0';
if(nread==0){
//表示客户端断开连接
/*
** 这里不能先close,应该先调用epoll_del,
** 因为先调用close关闭了文件描述符后,再调用epoll_del
** 内核将不能找到所要从内核事件表中移除的文件描述符
**/
printf("removing fd:%d\n",fd);
epoll_del(epfd,fd);//将内核事件表中fd删除
close(fd);
}else{
//客户端数据请求
printf("serving client on fd %d, read:%s", fd, buff);
ev.events=EPOLLOUT;
ev.data.fd=fd;
epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
for(i=0; i<nread; i++){
/*if(buf[i]>='a' && buf[i]<='z'){
buf[i] = buf[i] - 32;
}*/
buff[i] = toupper(buff[i]);
}
}
}else if(events[i].events&EPOLLOUT){
write(fd,buff,strlen(buff));
printf("serving send on fd %d, send:%s", fd, buff);
ev.events =EPOLLIN;
ev.data.fd=fd;
epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
}
}
}
}
}
return 0;
}
void epoll_add(int epfd,int fd){
//添加新的事件以及描述符到内核事件表
ev.events=EPOLLIN;
ev.data.fd =fd;
if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev)==-1){
perror("epoll_ctl_add error!\n");
}
}
void epoll_del(int epfd,int fd){
//删除事件以及描述符
if(epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL)==-1){
perror("epoll_ctl_del error!\n");
}
}
int create_sockfd(){
int ret;
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if (sockfd == -1)
{
return -1;
}
struct sockaddr_in server_addr;
memset(&server_addr,'\0',sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(SERVER_PORT);
server_addr.sin_addr.s_addr=inet_addr(SERVER_IP);
if(ret=bind(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1){
return -1;
}
listen(sockfd,5);
return sockfd;
}