epoll:为处理大量文件描述符,而改进的poll;
特点:每个文件描述符及事件是在结构体中,而且是存放在链表中,需要是添加,不需要时释放,不会占用多的空间;
三个系统调用函数:
头文件:#include <sys/epoll.h>
int epoll_create(int size);
功能:
创建epoll
参数:
int size:目前已经失效
返回值:
失败:-1,set errno
成功:文件描述符
所以epoll创建成功后,也会占用一个文件描述符,需要释放close(fd),否则会导致fd耗尽;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:
epoll的事件注册函数,不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是先要 在这里注册要监听的事件类型;注册、删除、修改epoll链表中的指定节点;
参数:
int epfd:epoll的文件描述符,创建函数的返回值
int op:命令码
EPOLL_CTL_ADD 注册一个监测对象
EPOLL_CTL_MOD 修改一个监测对象
EPOLL_CTL_DEL 删除一个监测对象
int fd:需要监听的对象(文件描述符)
struct epoll_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*/
}
其中events是以下一个或几个宏的集合
EPOLLIN:表示对应文件描述符可以读(包括对端SOCKET正常关闭)
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示带外数据到来)
EPOLLERR:表示对应的文件描述符发生错误
EPOLLHUP:表示对应的文件描述符被挂断
EPOLLET:将EPOLL设为边缘事件(多次触发,直到把数据读完为止)
EPOLLONESHOT:只监听一次事件,如果要监听需要把fd重新加入到epoll中;
返回值:
失败:-1,错误码在errno中
成功:文件描述符
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
功能:
等结果
参数:
int epfd:epoll的文件描述符
struct epoll_event *events:用于保存结构的事件数组(events不能是空指针)
int maxevents:告诉内核这个数组有多大
int timeout:超时时间
返回值:
-1:调用失败
0:超时
成功:返回实际发生的事件的数量
epoll的工作原理:返回实际发生事件文件描述符的数量,可以帮助你从数组中有效的找到什 么事件发生,而不用遍历整个数组,这里数组使用了mmap技术,不用再一次复制粘贴;
示例代码:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/epoll.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define PORT 17000
#define MAX 1023
int set_fcntl(int rws)
{
int flags = fcntl(rws,F_GETFD);
if(flags < 0) {
perror("get fcntl errnor");
return -1;
}
flags |= O_NONBLOCK;
if(fcntl(rws,F_SETFD,flags) < 0) {
perror("set fcntl errnor");
return -1;
}
return 0;
}
int main()
{
int sockfd;
struct sockaddr_in myaddr;
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0) {
perror("creat sockfd failed");
return -1;
}
printf("socket................................................\n");
int on = 1;
if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) {
perror("setsockopt");
return -1;
}
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(PORT);
myaddr.sin_addr.s_addr = INADDR_ANY;
if(bind(sockfd,(const struct sockaddr *)&myaddr,sizeof(myaddr)) < 0) {
perror("bind failed");
return -1;
}
printf("bind................................................\n");
if(listen(sockfd,10) < 0) {
perror("listen failed");
return -1;
}
//epoll fd
int efd = epoll_create(1);
if(efd < 0) {
printf("efd errno\n");
return -1;
}
printf("epoll creat.....................................\n");
//put listen_fd to epoll_fd
struct epoll_event evt = {
.events = EPOLLIN,
.data.fd = sockfd,
};
if(epoll_ctl(efd,EPOLL_CTL_ADD,sockfd,&evt) < 0){
printf("put listen_fd epoll errno\n");
return -1;
}
printf("listen fd add epoll.....................................\n");
char buf[1024] = {0};
struct epoll_event events[MAX];
while(1) {
int i = 0;
int num = epoll_wait(efd,events,MAX,~0);
printf("wait events.....................................\n");
if(num < 0) {
printf("epoll_wait events start errno\n");
return -1;
}
for(i = 0; i < num; i++ ) {
if(events[i].events & EPOLLIN) {
if(sockfd == events[i].data.fd) {
int cn_fd = accept(sockfd,NULL,NULL);
set_fcntl(cn_fd);
printf("accept................................................\n");
if(cn_fd < 0) {
printf("accept fd errnor\n");
return -1;
}
struct epoll_event ac_evt = {
.events = EPOLLIN,
.data.fd = cn_fd,
};
if(epoll_ctl(efd,EPOLL_CTL_ADD,cn_fd,&ac_evt) < 0){
printf("put accept_fd epoll errno\n");
return -1;
}
}
else {
int len = read(events[i].data.fd,buf,sizeof(buf));
if(len <= 0) {
struct epoll_event ac_evt1;
if(epoll_ctl(efd,EPOLL_CTL_DEL,events[i].data.fd,&ac_evt1) < 0){
printf("delete accept_fd epoll errno\n");
return -1;
}
close(events[i].data.fd);
}
else {
printf("%s\n",buf);
write(events[i].data.fd,buf,len);
}
}
}
}
}
}