多路复用-epoll

news/2024/10/25 8:16:21/

epoll的实现与应用

  • epoll的原理
  • 基本函数
    • epoll_create()
    • epoll_ctl()
    • epoll_wait
    • event事件
  • 用epoll搭建一个简单的服务器
  • 总结

epoll的原理

epoll底层使用了红黑树和链表的结合解决了select的最高限制,并且更高效的对多个IO同时进行操作。epoll中的红黑树和链表在内存中统一一份,就绪链表只是在红黑树的基础上,对节点进行连接指向。

课前小黑板

  • 事件触发的两种模型
  1. ET: 边沿触发;buf中无数据到有数据才触发
  2. LT:水平触发;只要buf有数据就一直触发
  • io三种状态
    可读、可写、是否出错
    可读可写的判断依据为recvbuf是否有值以及sendbuf是否为空

epoll默认为水平触发。如何选择使用哪种触发?一般来讲

  1. 当实际的大小大于buf大小的时候,选择LT模式
  2. listen监听的时候选择LT模式,因为如果选择ET模式的话,发生3个以上客户端同时连接时,有可能漏掉个把
我们都知道了select是遍历所有的IO口,再在fd_set 中判断该io口是否就绪。而epoll则在epoll_wait时获取的event,就是准备就绪的链表

基本函数

  • 头文件

sys/epoll.h

epoll_create()

  • 函数作用
    用于创建一个epoll实例,返回实例的fd;
  • 函数原型
int epoll_create(int size);
  • 参数介绍
参数名说明
size最大监听的数量。目前该参数无意义,只有是否大于零的区别,如果需要有监听,则填写一个大于零的数即可

epoll_ctl()

  • 函数作用
    用于添加或删除所要监听的socket。成功返回0,失败返回-1.错误值可在errno中获取。

  • 函数原型

int epoll_ctl(int epfd,int op,int fd,struct epoll_event * event);
  • 参数介绍
参数名说明
eptd需要操作的文件描述符,即一开始create的那个epollfd
op操作类型
EPOLL_CTL_ADD:注册目标文件描述符fd
EPOLL_CTL_MOD:更改与fd相关联的事件
EPOLL_CTL_DEL:删除epfd中的fd
fd所要操作的socketfd
eventevent事件,设置一些事件类型,比如可读触发,边沿触发这些,具体可参看event事件

epoll_wait

  • 函数作用
    等待通过epoll句柄fd找到的就绪事件。函数返回准备好的事件数量。

  • 函数原型

int epoll_wait(int _epfd, struct epoll_event *_events, int _maxevents, int _timeout)
  • 参数介绍
参数名说明
_epfd总的句柄
_events准备好的事件集合
_maxevents希望返回的最大事件数量,一般为events的数组大小
_timeout最大等待时长,单位为毫秒。-1==infinite

event事件

以下列出部分event事件,全部的话可在epoll.h中查看

事件名事件说明
EPOLLIN可读
EPOLLPRI有紧急的事件可以读
EPOLLOUT可写
EPOLLET边沿触发,默认为水平触发

用epoll搭建一个简单的服务器

#include <stdio.h>
#include <string.h>
#include <netinet/in.h>#include <sys/socket.h>
#include <sys/epoll.h>
#include <errno.h>
#include <arpa/inet.h>#define EPOLL_SIZE 1024
#define BUFFER_LENGTH 1024int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);printf("sockfd is %d\n", sockfd);struct sockaddr_in servAddr;memset(&servAddr, 0,sizeof(servAddr));servAddr.sin_family = AF_INET;servAddr.sin_port = htons(4022);servAddr.sin_addr.s_addr = INADDR_ANY;if(bind(sockfd, (struct sockaddr*)&servAddr,sizeof(struct sockaddr_in)) < 0){printf("bind sockfd error!\n");return -1;}if(listen(sockfd, 5) < 0){printf("listen error!\n");return -1;}int epollFd = epoll_create(EPOLL_SIZE);struct epoll_event ev, events[EPOLL_SIZE] = {0};ev.events = EPOLLIN;ev.data.fd = sockfd;epoll_ctl(epollFd, EPOLL_CTL_ADD, sockfd, &ev);while(1){int nReady = epoll_wait(epollFd, events, EPOLL_SIZE, -1);if (nReady == -1){printf("nReady error!\n");break;}for (int i = 0; i < nReady; i++){printf("2 \n");if (events[i].data.fd == sockfd){struct sockaddr_in cliAddr;memset(&cliAddr, 0, sizeof(cliAddr));int cliLen = sizeof(cliAddr);printf("1 :%d\n");int clifd = accept(sockfd, (struct sockaddr*)&cliAddr, &cliLen);printf("clifd :%d\n", clifd);if (clifd <= 0){continue;}char str[INET_ADDRSTRLEN] = {0};printf("recvfrom %s at port %d, sockfd:%d, clifd:%d\n", inet_ntop(AF_INET, &cliAddr.sin_addr, str, sizeof(str)),ntohs(cliAddr.sin_port), sockfd, clifd);//将接收到的io口也放入观察集中ev.events = EPOLLIN | EPOLLET;ev.data.fd = clifd;epoll_ctl(epollFd, EPOLL_CTL_ADD, clifd, &ev);}else{int clifd = events[i].data.fd;char buffer[BUFFER_LENGTH] = {0};int ret = recv(clifd, buffer, BUFFER_LENGTH, 0);if (ret < 0){if (errno == EAGAIN || errno == EWOULDBLOCK){printf("read all data ready!\n");}close(clifd);ev.events = EPOLLIN | EPOLLET;ev.data.fd = clifd;epoll_ctl(epollFd, EPOLL_CTL_DEL, clifd, &ev);}else if(ret == 0){printf("disconnect %d \n", clifd);close(clifd);ev.events = EPOLLIN | EPOLLET;ev.data.fd = clifd;epoll_ctl(epollFd, EPOLL_CTL_DEL, clifd, &ev);continue;}else{printf("recv: %s, %dBytes", buffer, ret);}}}}return 0;}

总结

 epoll 相较于 select来说在处理更多的io时,更为高效。这两种多路复用的原理都是将数据等待和读取数据分离开来。select用到了位图,而epoll使用的红黑树和链表。
 遗憾的是不同的操作系统特供的 epoll 接口有很大差异,所以使用类似于 epoll 的接口实现具有较好跨平台能力的服务器会比较困难。比如mac电脑系统就没有epoll,只有kqueue,与epoll应用类似。


http://www.ppmy.cn/news/1431780.html

相关文章

Ghost Buster Pro for Mac:强大的系统优化工具

Ghost Buster Pro for Mac是一款功能强大的系统优化工具&#xff0c;专为Mac用户设计&#xff0c;旨在提供全方位的系统清理、优化和维护服务。 Ghost Buster Pro for Mac v3.2.5激活版下载 这款软件拥有出色的垃圾清理能力&#xff0c;能够深度扫描并清除Mac上的无效目录、文件…

vue3第二十一节(新增编译宏defineExpose)

引言&#xff1a;在vue2中我们可以使用 this.$refs.xxx调用组件内部的属性或者方法&#xff0c;同时子组件也可以使用 this.$parent.xxx 调用父组件的属性和方法&#xff1b; 但是 当我们在setup 语法糖中&#xff0c;因为此时的组件默认是关闭即组件是私有的&#xff0c;故使…

Vue之v-on事件修饰符的含义及使用

背景&#xff1a;Vue 拆封了一个组件&#xff0c;在组件里面会使用一个方法来改变父组件传过来的值&#xff0c; 但是在子组件里面操作父组件的数据变更&#xff0c;实在比较麻烦&#xff08;因为单向数据流&#xff09;&#xff0c; So 能不能直接在组件上面绑定事件方法呢&…

HCIP-Datacom-ARST必选题库_22_SDWAN【1道题】

一、单选 1.SD-WAN解决方案适合如下哪个场景? 企业分支互联 企业数据中心网络内部互联 企业园区无线网络部署 略

使用OpenCV计算滑块缺口

1.参考文章:https://blog.csdn.net/qq_27371025/article/details/133072065 2.实现过程&#xff1a;接口中传入base64 图片&#xff0c;base64转化为image &#xff0c;通过图片获取缺口信息 实现步骤&#xff1a; 2.1 安装&#xff1a;cv2 opencv-python 是 OpenCV&#xff08…

Java 网络编程之TCP(一):基于BIO

环境&#xff1a; jdk 17 IntelliJ IDEA 2023.1.1 (Ultimate Edition) Windows 10 专业版 22H2 TCP&#xff1a;面向连接的&#xff0c;可靠的数据传送协议 Java中的TCP网络编程&#xff0c;其实就是基于常用的BIO和NIO来实现的&#xff0c;本文先讨论BIO&#xff1b; BIO…

uniapp微信小程序分包

一、创建分包文件夹subPack 二、将页面文件放入分包文件夹中 启动页面和导航tabBar页面不要放入分包文件夹中 三、配置pages.json 四、效果

1.微服务介绍

完整的微服务架构图 注册中心 配置中心 服务集群 服务网关 分布式缓存 分布式搜索 数据库集群 消息队列 分布式日志服务 系统监控链路追踪 Jenkins docker k8s 技术栈 微服务治理&#xff1a; 注册发现、远程调用、负载均衡、配置管理、网关路由、系统保护、流量…