epoll_create
int epoll_create (int size);
头文件 #include<sys/epoll.h>
函数说明 :创建一棵epoll树,返回一个树根节点
函数参数:
size:必须传一个大于0的数
返回值:返回一个文件描述符,这个文件描述符就表示epoll树的树根节点
epoll_ctl
int epoll_ctl(int epfd,int op,int fd,struct epoll_event* event);
函数说明:将fd上epoll树,从树上删除和修改
函数参数:
epfd:epoll树的树根节点
op:
EPOLL_CTL_ADD:上树操作
EPOLL_CTL_MOD:修改
EPOLL_CTL_DEL:从树上删除节点
fd:要操作的文件描述符
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 */
};
events:
EPOLLIN:可读事件
EPOLLOUT:可写事件
EPOLLERR:异常事件
event_fd:委托内核监控的文件描述符
struct epoll_event ev;
ev.events=EPOLLIN;
ev,data.fd=fd;
epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev);
epoll_wait
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
函数说明:委托内核监控epoll树的事件节点
函数参数:
epfd:epoll树根节点
events:传出参数,结构体数组
maxevents:events数组大小
timeout:-1:表示阻塞 0:表示不阻塞 >0:表示阻塞时长
函数返回的数组中的事件节点的值不会修改,是当时上epoll树的时候设置的值
使用poll模型开发服务端流程:
- 创建socket,得到监听文件描述符lfd–socket()
- 设置端口复用—setsockopt()
- 绑定bind–()
- 监听–listen()
- 创建一棵epoll树–int epfd=epoll_create(),返回树根节点
- 将监听文件描述符对应事件节点上树
- 进入循环,调用epoll_wait函数,让内核阻塞监控,成功时返回发生事件的文件描述符数,第二个参数用来保存发生事件的结构体数组地址
- 遍历返回的结构体数组中的内容,获取结构体中的文件描述符,若与lfd相等,则为客户端连接请求。若为其他,则读取文件描述符中的内容到缓存区,若读不到则关闭当前文件描述符,并将对应的事件节点下树
struct epoll_event ev;
ev.events=EPOLLIN;//可读事件
ev.data.fd=lfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
struct epoll_event events[1024];
while(1){
nready=epoll_wait(epfd,events,1024,-1);
if(nready<0){
if(errno==EINTR){
continue;
}
break;
}
for(i=0;i<nready;i++){
sockfd=events[i].data.fd;
//有客户端链接请求
if(sockfd==lfd){
cfd=accept(lfd,NULL,NULL);
//将cfd对应的读事件上epoll树
ev.data.fd=cfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
continue;
}
//有客户端发来数据
n=read(sockfd,buf,sizeof(buf));
if(n<=0){
close(sockfd);
//将sockfd对应的事件节点从epoll树上删除
epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL);
perror("read error or client closed");
continue;
}else{
write(sockfd,buf,n);
}
}
}
close(lfd);
return 0;