epoll的提出--》它所支持的文件描述符上限是系统可以最大打开的文件的数目;eg:1GB机器上,这个上限10万个左右。 每个fd上面有callback(回调函数)函数,只有活跃的fd才有主动调用callback,不需要轮询。 注意: Epoll处理高并发,百万级,不关心底层怎样实现,只需要会调用就可以。

函数接口
- #include <sys/epoll.h>
- int epoll_create(int size);
- 功能:创建红黑树根节点
- 参数:size:不作为实际意义值 >0 即可
- 返回值:成功时返回epoll文件描述符,失败时返回-1。
-
- int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
- 功能:控制epoll属性
- epfd:epoll_create函数的返回句柄。7、】=
- op:表示动作类型。有三个宏 来表示:
- EPOLL_CTL_ADD:注册新的fd到epfd中
- EPOLL_CTL_MOD:修改已注册fd的监听事件
- EPOLL_CTL_DEL:从epfd中删除一个fd
- Fd:需要监听的fd。
- event:告诉内核需要监听什么事件
- EPOLLIN:表示对应文件描述符可读
- EPOLLOUT:可写
- EPOLLPRI:有紧急数据可读;
- EPOLLERR:错误;
- EPOLLHUP:被挂断;
- EPOLLET:触发方式,边缘触发;(默认使用边缘触发)
- ET模式:表示状态的变化;
- 返回值:成功时返回0,失败时返回-1
-
- typedef union epoll_data
- {
- void* ptr;(无效)
- int fd;
- uint32_t u32;
- uint64_t u64;
- } epoll_data_t;
- struct epoll_event
- {
- uint32_t events; / * Epoll事件* /
- epoll_data_t data; / *用户数据变量* /
- };
- //等待事件到来
- int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
- 功能:等待事件的产生,类似于select的用法
- epfd:句柄;
- events:用来保存从内核得到事件的集合;
- maxevents:表示每次能处理事件最大个数;
- timeout:超时时间,毫秒,0立即返回,-1阻塞
- 成功时返回发生事件的文件描述个数,失败时返回-1
select,poll都属于 同步IO机制(轮询)
epoll属于异步IO机制(不轮询):
epoll的提出--》它所支持的文件描述符上限是系统可以最大打开的文件的数目;
eg:1GB机器上,这个上限10万个左右。
每个fd上面有callback(回调函数)函数,只有产生事件的fd才有主动调用callback,不需要轮询。
注意:
Epoll处理高并发,百万级

3.epoll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可.
Epoll的使用:
1.创建红黑树 和 就绪链表
2.添加文件描述符和事件信息到树上
3.阻塞等待事件的产生,一旦产生事件,则进行处理
4.根据链中准备处理的文件描述符 进行处理
epoll 要使用一组函数: epoll_create 创建红黑树 和 就序列表
epoll_ctl 添加文件描述符和事件到树上 / 从树上删除
epoll_wait 等待事件产生
- 头文件:#include
- 声明:int epoll_create(int size);
- 功能:创建红黑树根节点(创建epoll实例) , 同时也会创建就绪链表
- 返回值:成功时返回一个实例(二叉树句柄),失败时返回-1。
- 声明: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
-
- 功能:控制epoll属性,比如给红黑树添加节点
-
- 参数: 1. epfd: epoll_create函数的返回句柄。//一个标识符
- 2. op:表示动作类型,有三个宏:
- EPOLL_CTL_ADD:注册新的fd到epfd中
- EPOLL_CTL_MOD:修改已注册fd的监听事件
- EPOLL_CTL_DEL:从epfd中删除一个fd
- 3. 要操作的文件描述符
- 4. 结构体信息:
- typedef union epoll_data
- {
- int fd; //要添加的文件描述符
- uint32_t u32; typedef unsigned int
- uint64_t u64; typedef unsigned long int
- } epoll_data_t;
-
- struct epoll_event
- {
- uint32_t events; 事件
- epoll_data_t data; //共用体(看上面)
- };
-
- 关于events事件:
- EPOLLIN: 表示对应文件描述符可读
- EPOLLOUT: 可写
- EPOLLPRI:有紧急数据可读;
- EPOLLERR:错误;
- EPOLLHUP:被挂断;
- EPOLLET:触发方式,边缘触发;(默认使用边缘触发)
- ET模式:表示状态的变化;
- NULL: 删除一个文件描述符使用,无事件
-
- 返回值:成功:0, 失败:-1
-
- 声明: int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
-
- 功能:等待事件产生
- 内核会查找红黑树中有事件响应的文件描述符, 并将结构体放入就绪链表
- 就绪链表中的内容, 执行epoll_wait会同时复制到第二个参数events
-
- 参数: epfd:句柄;
- events:用来保存从就绪链表中响应事件的集合;
- maxevents: 表示每次在链表中拿取响应事件的个数;
- timeout:超时时间,毫秒,0立即返回 ,-1阻塞
-
- 返回值: 成功: 实际从链表中拿出的数目 失败时返回-1
-
- #include <stdio.h>
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netinet/ip.h> /* superset of previous */
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <sys/epoll.h>
- int main(int argc, char const *argv[])
- {
- if (argc < 2)
- {
- printf("plase input
\n" ); - return -1;
- }
- //1.创建套接字,用于链接
- int sockfd;
- int acceptfd;
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0)
- {
- perror("socket err");
- return -1;
- }
- printf("sockfd:%d\n", sockfd);
- //2.绑定 ip+port 填充结构体
- struct sockaddr_in saddr;
- saddr.sin_family = AF_INET; //协议族ipv4
- saddr.sin_port = htons(atoi(argv[1])); //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
- saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //ip地址,转化为16进制表示
- socklen_t len = sizeof(saddr); //结构体大小
- //bind绑定ip和端口
- if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
- {
- perror("bind err");
- return -1;
- }
- printf("bind success\n");
- //3.启动监听,把主动套接子变为被动套接字
- if (listen(sockfd, 1) < 0)
- {
- perror("listen err");
- return -1;
- }
- printf("listen success\n");
-
-
- //引入epoll
- //创建结构体变量
- struct epoll_event event;
- struct epoll_event revents[32];//存放epoll_wait拿取的内容
- //1.创建树
- int epfd=epoll_create(1);
- // if (epfd = epoll_creat(1) < 0)
- // {
- // perror(" creat err");
- // return -1;
- // }
- //2.将关心的文件描述符添加到树上
- //标准输入上树
- event.events = EPOLLIN | EPOLLET;
- event.data.fd = 0;
- epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &event);
- //套接字上树
- event.data.fd = sockfd;
- epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);
-
- char buf[64] = {0};//字符数组
- //3.在链表中取事件
-
- while (1)
- {
- //3.阻塞 等待文件描述符产生事件
- int ret = epoll_wait(epfd, revents, 10, -1);
- if (ret < 0)
- {
- perror(" epoll_wait err");
- return -1;
- }
- //4.根据文件描述符 进行处理
- for (int i = 0; i < ret; i++)
- {
- if (revents[i].data.fd == 0)
- {
- //5.执行操作
- fgets(buf, sizeof(buf), stdin);
- if (buf[strlen(buf)] == '\0') //去掉fgets补的'\n'
- {
- buf[strlen(buf) - 1] = '\0';
- }
- send(event.data.fd, buf, sizeof(buf), 0); //发送
-
- }
- else if (revents[i].data.fd == sockfd)
- {
- int acceptfd = accept(sockfd, (struct sockaddr *)&saddr, &len);
- if (acceptfd < 0)
- {
- perror("accept is err:");
- return -1;
- }
- printf("fd: %d ip: %s port: %d is connect\n",acceptfd, inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
-
- event.data.fd = acceptfd;//新的acceptfd上树
- event.events = EPOLLIN | EPOLLET;
- epoll_ctl(epfd, EPOLL_CTL_ADD, acceptfd, &event);
- }
- else
- {
- int recvbyte = recv(revents[i].data.fd, buf, sizeof(buf), 0);
- if (recvbyte < 0)
- {
- perror("recv is err:");
- return -1;
- }
- else if (recvbyte == 0)
- {
- printf("client is exit\n");
- close(revents[i].data.fd);
- epoll_ctl(epfd, EPOLL_CTL_DEL, revents[i].data.fd, NULL);
- }
- else
- {
- printf("%d : %s\n", revents[i].data.fd, buf);
- }
- }
- }
- }
- close(sockfd);
- close(acceptfd);
- return 0;
- }
-