【项目 计网11】4.29 epoll API介绍 4.30 epoll 代码编写 4.31 epoll的两种工作模式

news2025/1/20 21:48:39

4.29 epoll API介绍

在这里插入图片描述

epoll_create实例在内核区,创建了一个eventpoll结构体。这个函数的返回值是一个文件描述符,通过这个fd去操纵eventpoll

#include <sys/epoll.h>
//创建一个新的epoll实例。在内核中创建了一个数据,这个数据中有两个比较重要的数据,一个是需要检测的文件描述符的信息(红黑树),还有一个是就绪列表,存放检测到数据发送改变的文件描述符信息(双向链表)。
int epoll_create(int size);
	-参数:
		size:目前没有意义了,随便写一个数,必须大于0
	-返回值:
		-1:失败
		>0:文件描述符,操作epoll实例的

typedef union epoll_data{
	void *ptr;
	int fd;
	uint32_t u32;
	uint64_t u64;
}epoll_data_t;

struct epoll_event{
	uint32_t events;
	epoll_data_t data;
};
常见的EPOLL检测事件:
	-EPOLLIN
	-EPOLLOUT
	-EPOLLERR

//对epoll实例进行管理:添加文件描述符信息,删除信息,修改信息
int epoll_ctrl(int epfd,int op,int fd,struct epoll_event *event);
	-参数:
		-epfd:epoll实例对应的文件描述符
		-op:要进行什么操作(把fd添加进epfd这个表里,还是删除,还是要修改fd的什么信息)
			EPOLL_CTL_ADD:添加
			EPOLL_CTL_MOD:修改
			EPOLL_CTL_DEL:删除
		-fd:要检测的文件描述符
		-event:检测文件描述符什么事情(把fd放进epfd这个表以后,是要监听这个操作符什么事情)
		(觉得不清楚的话看一眼代码就知道了)
		

//检测函数
int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);
	-参数:
		-epfd:epoll实例对应的文件描述符
		-events:传出参数,保存了发送了变化的文件描述符的信息
		-maxevents:第二个参数结构体数组的大小
		-timeout:阻塞时间
			-0:不阻塞
			- -1:阻塞,直到检测到fd数据发生变化,解除阻塞
			- >0:阻塞时长(毫秒)
	-返回值:
		-成功,返回发送变化的文件描述符的个数>0
		-失败 -1

联合体和结构体的区别

4.30 epoll 代码编写

client.c

#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(){
    //创建socket
    int fd=socket(PF_INET,SOCK_STREAM,0);
    if(fd==-1){
        perror("socket");
        return -1;
    }

    struct sockaddr_in seraddr;
    inet_pton(AF_INET,"127.0.0.1",&seraddr.sin_addr.s_addr);
    seraddr.sin_family=AF_INET;
    seraddr.sin_port=htons(9999);

    //连接服务器
    int ret=connect(fd,(struct sockaddr *)&seraddr,sizeof(seraddr));
    if(ret==-1){
        perror("connect");
        return -1;
    }

    int num=0;
    while(1){
        char sendBuf[1024]={0};
        sprintf(sendBuf,"send data %d",num++);
        //发送数据
        write(fd,sendBuf,strlen(sendBuf)+1);

        //接收数据
        int len=read(fd,sendBuf,sizeof(sendBuf));
        if(len==-1){
            perror("read");
            return -1;
        }else if(len>0){
            printf("read buf=%s\n",sendBuf);
        }else{
            printf("服务器已经断开连接...\n");
            break;
        }
        sleep(1);
    }

    //关闭连接
    close(fd);

    return 0;
}

epoll.c

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>

int main(){

    //创建socket
    int lfd=socket(PF_INET,SOCK_STREAM,0);
    struct sockaddr_in saddr;
    saddr.sin_port=htons(9999);
    saddr.sin_family=AF_INET;
    saddr.sin_addr.s_addr=INADDR_ANY;

    //绑定
    bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));

    //监听
    listen(lfd,8);

    //调用epoll_create()创建一个epoll实例
    int epfd=epoll_create(100);

    //讲监听的文件描述符相关的检测信息添加到epoll实例中
    struct epoll_event epev;
    epev.events=EPOLLIN;
    epev.data.fd=lfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);

    struct epoll_event epevs[1024];

    while(1){
        int ret=epoll_wait(epfd,epevs,1024,-1);
        if(ret==-1){
            perror("epoll_wait");
            exit(-1);
        }

        printf("ret=%d\n",ret);

        for(int i=0;i<ret;++i){
            int curfd=epevs[i].data.fd;

            if(curfd==lfd){
                //监听的文件描述符有数据达到,有客户端连接
                struct sockaddr_in cliaddr;
                int len=sizeof(cliaddr);
                int cfd=accept(lfd,(struct sockaddr *)&cliaddr,&len);

                epev.events=EPOLLIN;
                epev.data.fd=cfd;
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
            }else{
                //有数据到达,需要通信
                char buf[1024]={0};
                //接收数据
                int len=read(curfd,buf,sizeof(buf));
                if(len==-1){
                    perror("read");
                    return -1;
                }else if(len==0){
                    printf("client closed...\n");
                    epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
                    close(curfd);
                }else if(len>0){
                    printf("read buf=%s\n",buf);
                    //发送数据
                    write(curfd,buf,strlen(buf)+1);
                }
            }
        }
    }

    close(lfd);
    close(epfd);

    return 0;
}

4.31 epoll的两种工作模式

  • LT模式(水平触发)
    假设委托内核检测读事件->检测fd的读缓冲区
    读缓冲区有数据->epoll检测到了会给用户通知
    a.用户不读数据,数据一直在缓冲区,epoll会一直通知
    b.用户只读了一部分数据,epoll会通知
    c.缓冲区的数据读完了,不通知

LT(level triggered)是缺省(默认)的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果不做任何操作,内核还是会继续通知你的。

  • ET模式(边沿触发)
    假设委托内核检测读事件->检测fd的读缓冲区
    读缓冲区有数据->epoll检测到了会给用户通知
    a.用户不读数据,数据一直在缓冲区,epoll下次检测的时候就不通知了
    b.用户只读了一部分数据,epoll不通知
    c.缓冲区的数据读完了,不通知

ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变成就绪时,内核通知epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。如果一直不对这个fd做IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。

ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT效率高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄(文件描述符)的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

client.c

#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(){
    //创建socket
    int fd=socket(PF_INET,SOCK_STREAM,0);
    if(fd==-1){
        perror("socket");
        return -1;
    }

    struct sockaddr_in seraddr;
    inet_pton(AF_INET,"127.0.0.1",&seraddr.sin_addr.s_addr);
    seraddr.sin_family=AF_INET;
    seraddr.sin_port=htons(9999);

    //连接服务器
    int ret=connect(fd,(struct sockaddr *)&seraddr,sizeof(seraddr));
    if(ret==-1){
        perror("connect");
        return -1;
    }

    int num=0;
    while(1){
        char sendBuf[1024]={0};
        fgets(sendBuf,sizeof(sendBuf),stdin);

        //发送数据
        write(fd,sendBuf,strlen(sendBuf)+1);

        //接收数据
        int len=read(fd,sendBuf,sizeof(sendBuf));
        if(len==-1){
            perror("read");
        }else if(len>0){
            printf("read buf=%s\n",sendBuf);
        }else{
            printf("服务器已经断开连接...\n");
            break;
        }
    }
    close(fd);
    return 0;
}

epoll_lt.c

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>

int main(){
    //创建socket
    int lfd=socket(PF_INET,SOCK_STREAM,0);
    struct sockaddr_in saddr;
    saddr.sin_port=htons(9999);
    saddr.sin_family=AF_INET;
    saddr.sin_addr.s_addr=INADDR_ANY;

    //绑定
    bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));

    //监听
    listen(lfd,8);

    //调用epoll_create()创建一个epoll实例
    int epfd=epoll_create(100);

    //将监听的文件描述符相关的检测信息添加到epoll实例
    struct epoll_event epev;
    epev.events=EPOLLIN;
    epev.data.fd=lfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);

    struct epoll_event epevs[1024];

    while(1){
        int ret=epoll_wait(epfd,epevs,1024,-1);
        if(ret==-1){
            perror("epoll_wait");
            exit(-1);
        }

        printf("ret=%d\n",ret);

        for(int i=0;i<ret;++i){
            int curfd=epevs[i].data.fd;

            if(curfd==lfd){
                //监听的文件描述符有数据达到,有客户端连接
                struct sockaddr_in cliaddr;
                int len=sizeof(cliaddr);
                int cfd=accept(lfd,(struct sockaddr *)&cliaddr,&len);

                epev.events=EPOLLIN;
                epev.data.fd=cfd;
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
            }else{
                if(epevs[i].events&EPOLLOUT){
                    continue;
                }
                //有数据到达,需要通信
                char buf[5]={0};
                int len=read(curfd,buf,sizeof(buf));
                if(len==-1){
                    perror("read");
                    exit(-1);
                }else if(len==0){
                    printf("client closed...\n");
                    epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
                    close(curfd);
                }else if(len>0){
                    printf("read buf=%s\n",buf);
                    write(curfd,buf,strlen(buf)+1);
                }
            }
        }
    }

    close(lfd);
    close(epfd);

    return 0;
}

epoll_et.c

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>

int main() {

    // 创建socket
    int lfd = socket(PF_INET, SOCK_STREAM, 0);
    struct sockaddr_in saddr;
    saddr.sin_port = htons(9999);
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;

    // 绑定
    bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));

    // 监听
    listen(lfd, 8);

    // 调用epoll_create()创建一个epoll实例
    int epfd = epoll_create(100);

    // 将监听的文件描述符相关的检测信息添加到epoll实例中
    struct epoll_event epev;
    epev.events = EPOLLIN;
    epev.data.fd = lfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev);

    struct epoll_event epevs[1024];

    while(1) {

        int ret = epoll_wait(epfd, epevs, 1024, -1);
        if(ret == -1) {
            perror("epoll_wait");
            exit(-1);
        }

        printf("ret = %d\n", ret);

        for(int i = 0; i < ret; i++) {

            int curfd = epevs[i].data.fd;

            if(curfd == lfd) {
                // 监听的文件描述符有数据达到,有客户端连接
                struct sockaddr_in cliaddr;
                int len = sizeof(cliaddr);
                int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);

                // 设置cfd属性非阻塞,read非阻塞
                int flag = fcntl(cfd, F_GETFL);
                flag | O_NONBLOCK;
                fcntl(cfd, F_SETFL, flag);

                epev.events = EPOLLIN | EPOLLET;    // 设置边沿触发
                epev.data.fd = cfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &epev);
            } else {
                if(epevs[i].events & EPOLLOUT) {
                    continue;
                }  

                // 循环读取出所有数据
                char buf[5];
                int len = 0;
                while( (len = read(curfd, buf, sizeof(buf))) > 0) {
                    // 打印数据
                    // printf("recv data : %s\n", buf);
                    write(STDOUT_FILENO, buf, len);//write写到终端
                    //回写数据
                    write(curfd, buf, len);
                }
                if(len == 0) {
                    printf("client closed....");
                }else if(len == -1) {
                    if(errno == EAGAIN) {
                        //所有数据已经读完
                        printf("data over.....");
                    }else {
                        //退出
                        perror("read");
                        exit(-1);
                    }
                    
                }

            }

        }
    }

    close(lfd);
    close(epfd);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/996975.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MergeTree表的三种格式

MergeTree表的三种格式 MergeTree表引擎有三种格式&#xff1a;Compact、Wide和In-memory&#xff0c;前两个为主要格式。具体区别是&#xff1a; Compact - 所有的列的数据放在一个文件&#xff1b;Wide - 每个列的数据放在一个文件&#xff1b;In-memory - 数据存在内存中&…

muduo 32 muduo项目总结

Timestamp时间管理类 ①&#xff1a;主要提供now函数显示当前时间&#xff1a;自1970年1月1日0点以来经过的秒数&#xff0c;使用time函数 ②&#xff1a; toString函数将字符串转化成时间字符串&#xff0c;使用localtime函数将秒数格式化成日历时间 解析tm_time 并以日历格…

JavaFX之Stage

Stage&#xff08;舞台&#xff09;&#xff0c;它代表了一个顶级窗口&#xff0c;是JavaFX应用程序的主要容器。Stage可以包含多个场景&#xff08;Scene&#xff09;&#xff0c;每个场景可以包含各种用户界面元素&#xff08;如按钮、文本框等&#xff09;。Stage提供了许多…

Android Studio配置

Android Studio安装包下载安装 安装包下载 进入网站 https://developer.android.google.cn/studio 下载Android Studio安装包&#xff08;本文版本android-studio-2021.1.1.22-windows&#xff09; 点击按钮之后会弹出条款&#xff0c;点击同意 安装包安装 点击next 选择…

Redis(详细)

目录 Redis是什么 Redis的主要特点 Redis的使用场景 会话存储 缓存存储 实现分布式锁 Redis为什么这么快 基于内存操作 高效的数据结构 多路I/O复用模型 单线程执行 Redis常见的数据结构 Redis有序列表的实现 跳跃表的执行流程 Redis分布式锁实现 使用分布式锁…

vue学习之属性绑定

内容渲染 采用 &#xff1a;进行属性渲染创建 demo3.html,内容如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"&…

Kafka/Spark-01消费topic到写出到topic

1 Kafka的工具类 1.1 从kafka消费数据的方法 消费者代码 def getKafkaDStream(ssc : StreamingContext , topic: String , groupId:String ) {consumerConfigs.put(ConsumerConfig.GROUP_ID_CONFIG , groupId)val kafkaDStream: InputDStream[ConsumerRecord[String, Strin…

centos7安装airflow2.7.1

python3安装 版本 Python-3.9.17 ./configure --prefix/usr/local/python3 make && make install随后用ln -s短链接python3和pip3 airflow安装 版本2.7.1 export AIRFLOW_HOME~/airflow编写一个sh文件 AIRFLOW_VERSION2.7.1# Extract the version of Python you…

C 风格文件输入/输出---直接输入/输出---(std::fread)---(std::fwrite)

C 标准库的 C I/O 子集实现 C 风格流输入/输出操作。 <cstdio> 头文件提供通用文件支持并提供有窄和多字节字符输入/输出能力的函数&#xff0c;而 <cwchar>头文件提供有宽字符输入/输出能力的函数。 从直接输入/输出 文件读取 std::fread 从给定输入流 stream …

基于ESP32设计可以通过 WiFi 控制的基于 ESP32 的定制四轴飞行器

介绍 我想选择一个涉及物联网概念的项目,例如无线通信和服务器端脚本编写。我最终决定建造一架四轴飞行器,使用定制的机载飞行控制器进行飞行,该控制器通过 WiFi 从触摸屏平板电脑接收操纵命令。该项目的最终目标是让四轴飞行器从相机图像中跟踪移动物体并跟随它。 硬件 对…

正则表达式:实数

正则表达式&#xff1a;实数 校验字符串&#xff0c;为有效的实数。 可以为&#xff1a;正数或负数&#xff1b; 可以为&#xff1a;整数或小数&#xff1b; 但是&#xff0c;不可以为非数值型的字符串&#xff0c;不可以是一连串的“0” 。 原始正则表达式 ^-?(0|[1-9]\d…

高可用Kuberbetes部署Prometheus + Grafana

概述 阅读官方文档部署部署Prometheus Grafana GitHub - prometheus-operator/kube-prometheus at release-0.10 环境 步骤 下周官方github仓库 git clone https://github.com/prometheus-operator/kube-prometheus.git git checkout release-0.10 进入工作目录 cd kube…

GDB的C++调试方法

本文记录基础的GDB调试过程&#xff0c;包含指令如下&#xff1a; 文章目录 准备编译文件GDB启动GDB开启代码行设置断点运行程序查看pc的指令查看监视的变量以及断点设置快照checkpoint实验1实验2 nextnextistepx/i $pcfinishinfo break 和 delete Numrefbreak col. if condit…

浅谈原型链

一.在掌握原型链之前首先要了解这三点 1.每个函数都有prototype这个属性我们称为原型对象 2.每个对象都有__proto__这个属性 3.对象的__proto__可以访问原型对象上的方法和变量,如果访问不了,就会向上进行查找,直到找不到为止,会出现报错的情况l。 二.例子 1.代码: let arr …

云计算与虚拟化

一、概念 什么是云计算&#xff1f; 云计算&#xff08;cloud computing&#xff09;是分布式计算的一种&#xff0c;指的是通过网络“云”将巨大的数据计算处理程序分解成无数个小程序&#xff0c;然后&#xff0c;通过多部服务器组成的系统进行处理和分析这些小程序得到结果…

基于folium绘制黑河腾冲线,胡焕庸线

背景 黑河腾冲线&#xff0c;又名胡焕庸线&#xff0c;是我们人口密度分布的的近似分界线。今天基于folium&#xff0c;使用python来绘制这条线。 代码 # -*- coding:UTF-8 -*-# region 引入必要依赖 from selfPyTools.mapModule import * # endregion# 准备一个地图类对象,…

学习Bootstrap 5的第十一天

折叠 基础的折叠 在 Bootstrap 5 中&#xff0c;折叠效果可以通过添加特定的属性和类来轻松实现内容的显示和隐藏。具体步骤如下&#xff1a; 1、创建一个可折叠的元素&#xff0c;通常使用 <div> 标签&#xff0c;并为其添加 .collapse 类&#xff0c;以指示它是可折…

智能化时代前端开发使用Amazon CodeWhisperer在vscode中编写代码

目录 一、概述 1.Amazon CodeWhisperer使用您的 AI 编码配套应用程序更快、更安全地构建应用程序。 2.CodeWhisperer 经过数十亿行代码的训练&#xff0c;可以根据您的评论和现有代码实时生成从代码片段到全函数的代码建议。绕过耗时的编码任务&#xff0c;加速使用不熟悉的 …

【自学开发之旅】Flask-数据查询-数据序列化-数据库关系(四)

db.session ProductInfo.query filter() 灵活查询 filter_by() limit() 限制输出条目 offset() 偏移量 order_by() 排序 group_by() 分组聚合 <模型类>.query.<过滤方法> 过滤方法 查询方法 “牛”字开头且&#xff08;“,”默认&#xff09;价格大于5的 &g…

JS判断当前是早上,中午,下午还是晚上

<!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title></head><body><div></div><script>function getTimeState() {// 获取当前时间let timeNow new Date();// 获取当前小时let…