#include <sys/epoll.h>
//创建一个新的epoll实例。在内核中创建了一个数据,这个数据中有两个比较重要的数据,一个是需要检测的文件描述符的信息(红黑树),还有一个就绪列表,存放检测到数据发送改变的文件描述符信息(双向链表)。
int epoll_create(int size);
-参数:
-size:目前没有意义,随便一个数字,但必须大于0;
-返回值:
-1:失败
>0:文件描述符,操作epoll实例的。
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
-功能:对epoll实例进行管理;添加文件描述符信息,删除信息,修改信息。
-参数:
-epfd:epoll实例对应的文件描述符
-op:要进行什么操作
EPOLL_CTL_ADD:添加
EPOLL_CTL_DEL:删除
EPOLL_CTL_MOD:修改
-fd:要检测的文件描述符
-event:要检测的文件描述符什么事情
struct epoll_event{
uint32_t events;//Epoll event
epoll_data_t data;//User data variable
};
常见的Epoll检测事件:
-EPOLLIN
-EPOLLOUT
-EPOLLERR
typedef union epoll_data{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
}epoll_data_t;
int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);
-功能:检测函数
-参数:
-epfd:epoll实例对应的文件描述符
-events:传出参数,保存发送变化了的文件描述符的信息
-maxevents:第二个参数结构体数组的大小
-timeout:阻塞时间,
-0:不阻塞
- -1:阻塞,直到检测到fd数据发生变化时解除阻塞
- >0:阻塞时长(毫秒)
-返回值:
-成功:返回发送变化的文件描述符的个数>0
-失败:返回-1
epoll用法三要素:
1、int epfd=epoll_create()系统调用,此调用会返回一个值epfd,之后所有的函数都会使用这个epfd;
工作原理:当某一进程调用epoll_create()时,内核会调用eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。
struct eventpoll{ //红黑树的根节点,树中存储着所有添加到epoll中需要监控的事件 struct rb_root rbr; //双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件 struct list_head rdlist; }
2、epoll_ctl(),通过这个调用可以向epoll中添加、删除、修改我们想要的事件,返回0,则成功,返回-1则失败;
3、epoll_wait(),通过此调用收集在epoll监控中已经发生的事件。
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/epoll.h>
int main()
{
//1、socket
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(lfd==-1)
{
perror("socket");
exit(-1);
}
//2、绑定bind
struct sockaddr_in saddr;
saddr.sin_family=AF_INET;
saddr.sin_port=htons(9999);
saddr.sin_addr.s_addr=INADDR_ANY;
in bfd=bind(sfd,(struct sockaddr *)&saddr,sizeof(saddr));
//3、监听listen
listen(sfd,128);
//4、创建epoll_wait一个实例
int epfd=epoll_create(120);
//将监听得文件描述符相关得检测信息添加到epoll实例中
struct epoll_event epev;
epev.events=EPOLLIN;
epev.data.fd=sfd;
//对epoll实例进行管理,添加文件描述符信息
epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&epev);
struct epoll_event epevs[1024];
while(1)
{
int ret=epoll_wait(epfd,epevs,1024,-1);
//返回发送变化的文件描述符的个数
if(ret==-1)
{
perror("epoll_wait");
exit(-1);
}
printf("ret=%d\n",ret);//输出文件描述符的个数
for(int i=0;i<ret;i++)
{
int curfd=epevs[i].data.fd;
if(curfd==sfd)
{
//监听的文件描述符有数据到达,与客户端连接
struct sockaddr_in cliaddr;
int len=sizeof(cliaddr);
int cfd=accept(sfd,(struct sockaddr *)&cliaddr,&len);
epev.events=EPOLLIN;
epev.data.fd=cfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
}
else
{
char buf[1024]={0};
int len=read(curfd,buf,sizeof(buf));
if(len==-1)
{
perror("read");
exit(-1);
}
else if(len==0)
{
printf("read closed...\n");
epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
close(curfd);
}
else if(len>0)
{
printf("read recv:%s\n",buf);
write(curfd,buf,strlen(buf)+1);
}
}
}
}
close(cfd);
close(epfd);
return 0;
}