epoll的使用方法及代码

一、epoll函数组解析
1、epoll_create函数
函数原型

int epoll_create(int size)

参数解释
(1)size:此参数在现在已经没有是什么意义了
(2)返回值:返回值为一个文件描述符,作为后面两个函数的参数
函数作用
此函数可以在内核中创建一个内核事件表,通过返回的内核事件表来管理
2、epoll_ctl函数
函数原型
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

参数解释
(1)epfd:操作内核时间表的文件描述符,即epoll_create函数的返回值
(2)op:操作内核时间表的方式
有以下操作方式:
(1)EPOLL_CTL_ADD(向内核时间表添加文件描述符,即注册)
(2)EPOLL_CTL_MOD(修改内核事件表事件)
(3)EPOLL_CTL_DEL (删除内核事件表中的事件)
(3)fd:操作的文件描述符
(4)event:指向struct epoll_event的指针
【epoll的使用方法及代码】event结构
struct epoll_event { uint32_t events; epoll_data_t data; }; typedef union epoll_data { void *ptr; int fd; _uint32_t u32; _uint64_t u64; }epoll_data_t;

结构体变量解释
(1)events:储存用户感兴趣的事情和就绪事件
(2)data:为一个联合体,联合体最重要的就是fd,即要操作的文件描述符
events取值
(1)EPOLLOUT:表示对应的文件描述符可以写;
(2)EPOLLPRI:表示对应的文件描述符有紧急的数据可读
(3)EPOLLERR:表示对应的文件描述符发生错误;
(4)EPOLLHUP:表示对应的文件描述符被挂断;
(5)EPOLLET:表示对应的文件描述符设定为edge模式;
函数作用
此函数可以增,删,改内核事件表中的事件
3、epoll_wait函数
函数原型
int epoll_wait(int epfd, struct epoll_event events, int maxevents, int timeout)

参数解释
(1)epfd:同上面函数
(2)events:用于接收内核返回的就绪事件的数组
(3)maxevents:用户最多能处理的事件个数
(4)等待I/O的超时值(后面的编程设为-1,表示永不超时),单位为ms
(5)返回值,指的是就绪事件的个数
函数作用
此函数在内核中实现,若有就绪事件,内核就会向用户返回已就绪的事件(注意:这里与poll不同,pol只返回已就绪事件的个数)
二、epoll编程代码及注释
#define _GNU_SOURCE #define MAXFD 100#include #include #include #include #include #include #include #include #include #include int create_socket()//创建一个socket连接 { int listenfd = socket(AF_INET, SOCK_STREAM, 0); assert(-1 != listenfd); struct sockaddr_in ser; ser.sin_family = AF_INET; ser.sin_port = htons(6000); ser.sin_addr.s_addr = inet_addr("127.0.0.1"); int res = bind(listenfd, (struct sockaddr *)&ser, sizeof(ser)); assert(-1 != res); listen(listenfd, 5); return listenfd; }void get_client_link(int fd, int epfd)//获取一个客户端连接 { struct sockaddr_in cli; int len = sizeof(cli); int c = accept(fd,(struct sockaddr_in *)&cli, &len); struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = c; epoll_ctl(epfd, EPOLL_CTL_ADD, c, &ev); printf("one client link\n"); }void unlink_client(int fd, int epfd)//断开连接 { close(fd); epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); printf("one client unlink\n"); }void deal_client_data(int fd, int epfd)//处理客户端数据 { int buff[128] = {0}; int res = recv(fd, buff, 127, 0); if(res == 0) { unlink_client(fd, epfd); return; } printf("%s\n",buff); send(fd, "ok", 2, 0); }void deal_finish_fd(int n, struct epoll_event *events, int epfd, int listenfd)//处理就绪的文件描述符 { int i = 0; for(i = 0; i <= n; i++) { int fd = events[i].data.fd; if(fd == listenfd) { get_client_link(fd, epfd); } else if(events[i].events & EPOLLHUP) { unlink_client(fd, epfd); } else if(events[i].events & EPOLLIN) { deal_client_data(fd, epfd); //在后面,如果n = 0,需要断开连接,所以得把epfd传进去 } else { printf("fd error\n"); } } }int main() { int listenfd = create_socket(); int epfd = epoll_create(5); struct epoll_event ev; ev.data.fd = listenfd; ev.events = EPOLLIN; epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev); //在内核事件中注册listenfd while(1) { struct epoll_event events[MAXFD]; int n = epoll_wait(epfd, events, MAXFD, -1); if(n < 0) { printf("epoll error\n"); break; }deal_finish_fd(n, events, epfd, listenfd); } exit(0); }

当然这是份儿服务器代码,如果要检测的话得需要一分儿客户端代码,这这里就不过多的赘述了,
有兴趣的读者可以自己编写测试一下,一下是我的测试结果:
epoll的使用方法及代码
文章图片

epoll的使用方法及代码
文章图片

三、epoll的优缺点
1、支持打开大数目的socket描述符(相对于select)
2、I\O效率不随fd数目的增加而线性下降(因为每次内核返回的是已就绪的文件描述符)
以上就是epoll的使用以及代码,欢迎读者纠错

    推荐阅读