Linux网络编程:IO多路复用——epoll

2023-10-29

服务器端代码:

/*

    epoll:event poll

    #include <sys/epoll.h>


    // epoll_create():创建一个新的epoll实例。在内核中创建了一个数据,这个数据中有两个比较重要的数据
    一个是需要检测的文件描述符的信息(红黑树):
    struct rb_root rbr;//需要监听的fd, red and black

    一个是就绪列表,存放检测到数据发送改变的文件描述符信息(双向链表):
    struct list_head rblist;//ready

    int epoll_create(int size);//在内核内创建
        - 参数:
            size : 目前没有意义了。随便写一个数,必须大于0
        - 返回值:
            -1 : 失败
            > 0 : 文件描述符,操作epoll实例的


    // 对epoll实例进行管理:添加文件描述符信息,删除信息,修改信息
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
        - 参数:
            - epfd : epoll实例对应的文件描述符,epoll_create()的返回值
            - op : 要进行什么操作
                EPOLL_CTL_ADD:  添加
                EPOLL_CTL_MOD:  修改
                EPOLL_CTL_DEL:  删除
            - fd : 要检测的文件描述符
            - event : 检测文件描述符什么事情

        常见的Epoll检测事件:
                - EPOLLIN:读,常用
                - EPOLLOUT:写
                - EPOLLERR:错误

        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 
        };


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



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

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

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

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

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


    
*/

#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实例中
    // epev:保存要监听的事件和fd
    struct epoll_event epev;
    epev.events = EPOLLIN;  //监听读事件
    epev.data.fd = lfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev);

    //epevs[]:传出数组,保存发生了监听事件的数组,用于用户态操作,大小自定义
    struct epoll_event epevs[1024];

    while(1) {

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

        // ret:监听到发生了事件的个数
        printf("ret = %d\n", ret);

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

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

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

                epev.events = EPOLLIN;
                //  epev.events = EPOLLIN | EPOLLOUT;   //多事件

                epev.data.fd = cfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &epev);
            } else {
                // 忽略EPOLLOUT
                // if(epevs[i].events & (EPOLLOUT | !EPOLLIN)) {
                //     continue;
                // } 

                // 本程序是针对的EPOLLIN事件,如果是多事件这里的判断要根据对应的事件宏值更改位运算判断
                // 有数据到达,通信
                char buf[1024] = {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 客户端


#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, "192.168.56.101", &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;
        }
        usleep(1000);//微秒
    }

    close(fd);

    return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux网络编程:IO多路复用——epoll 的相关文章

  • 批量删除文件名中包含 BASH 中特殊字符的子字符串

    我的目录中有一个文件列表 opencv calib3d so2410 so opencv contrib so2410 so opencv core so2410 so opencv features2d so2410 so opencv
  • Google BQ:运行参数化查询,其中参数变量是 BQ 表目标

    我正在尝试从 Linux 命令行为 BQ 表目标运行 SQL 此 SQL 脚本将用于多个日期 客户端和 BQ 表目标 因此这需要在我的 BQ API 命令行调用中使用参数 标志 parameter 现在 我已经点击此链接来了解参数化查询 h
  • python获取上传/下载速度

    我想在我的计算机上监控上传和下载速度 一个名为 conky 的程序已经在 conky conf 中执行了以下操作 Connection quality alignr wireless link qual perc wlan0 downspe
  • 通过 Visual Studio 2017 使用远程调试时 Linux 控制台输出在哪里?

    我的Visual Studio 2017 VS2017 成功连接Linux系统 代码如下 include
  • 就分页分段内存而言的程序寿命

    我对 x86 Linux 机器中的分段和分页过程有一个令人困惑的概念 如果有人能澄清从开始到结束所涉及的所有步骤 我们将很高兴 x86 使用分页分段内存技术进行内存管理 任何人都可以解释一下从可执行的 elf 格式文件从硬盘加载到主内存到它
  • 如何在linux中以编程方式获取dir的大小?

    我想通过 C 程序获取 linux 中特定目录的确切大小 我尝试使用 statfs path struct statfs 但它没有给出确切的大小 我也尝试过 stat 但它返回任何目录的大小为 4096 请建议我如何获取 dir 的确切大小
  • 如何获取 (Linux) 机器的 IP 地址?

    这个问题和之前问的几乎一样如何获取本地计算机的IP地址 https stackoverflow com questions 122208 get the ip address of local computer 问题 但是我需要找到一个的I
  • 为什么 fopen("any_path_name",'r') 不给出 NULL 作为返回值?

    在调试一些代码时 我得到如下内容 include
  • 并行运行 shell 脚本

    我有一个 shell 脚本 打乱大型文本文件 600 万行和 6 列 根据第一列对文件进行排序 输出 1000 个文件 所以伪代码看起来像这样 file1 sh bin bash for i in seq 1 1000 do Generat
  • 使用 shell 脚本将行附加到 /etc/hosts 文件

    我有一个新的 Ubuntu 12 04 VPS 我正在尝试编写一个安装脚本来完成整个 LAMP 安装 我遇到问题的地方是在 etc hosts文件 我当前的主机文件如下所示 127 0 0 1 localhost Venus The fol
  • 在 Mono 上运行 .Net MVC5 应用程序

    我正在 Windows 上的 Visual Studio 2013 中开发 Net 4 5 1 MVC5 应用程序 现在我想知道 是否可以在Linux Ubuntu 12 04 上运行这个应用程序 可以使用OWIN吗 Owin 可以自托管运
  • 静态方法的 Java 内存模型

    我来自操作系统和 C 语言背景 在代码编译时 世界很简单 需要处理和理解堆栈 堆文本部分等 当我开始学习 Java 时 我确实了解 JVM 和垃圾收集器 我对静态方法感到很有趣 根据我的理解 类的所有实例都会在堆中创建 然后被清理 但是 对
  • 配置tomat的server.xml文件并自动生成mod_jk.conf

    我在用apache 2 2 15 and tomcat6 6 0 24 on CentOS 6 4并希望使用 tomcat 服务器的功能 通过添加以下内容自动生成 mod jk conf 文件
  • Intel 上的 gcc 中的 _mm_pause 用法

    我参考过这个网页 https software intel com en us articles benefitting power and performance sleep loops https software intel com
  • 如何查找哪个 Yocto 项目配方填充图像根文件系统上的特定文件

    我经常与 Yocto 项目合作 一个常见的挑战是确定文件为何 或来自什么配方 包含在 rootfs 中 这有望从构建系统的环境 日志和元数据中得出 理想情况下 一组命令将允许将文件链接回源 即配方 我通常的策略是对元数据执行搜索 例如gre
  • Linux 为一组进程保留一个处理器(动态)

    有没有办法将处理器排除在正常调度之外 也就是说 使用sched setaffinity我可以指示线程应该在哪个处理器上运行 但我正在寻找相反的情况 也就是说 我想从正常调度中排除给定的处理器 以便只有已明确调度的进程才能在那里运行 我还知道
  • linux下如何从文本文件中获取值

    我有一些文本格式的文件 xxx conf 我在这个文件中有一些文本 disablelog 1 当我使用 grep r disablelog oscam conf 输出是 disablelog 1 但我只需要值1 请问你有什么想法吗 一种方法
  • Linux/POSIX:为什么 fork() 不分叉*所有*线程

    众所周知 POSIX下创建新进程的默认方式是使用fork 在 Linux 下 这在内部映射到clone 我想知道的是 众所周知 当一个人打电话时fork 子进程是用单个线程创建的 调用的线程fork cf https linux die n
  • 如何让 Node.js 作为后台进程运行并且永不死掉?

    我通过 putty SSH 连接到 linux 服务器 我尝试将其作为后台进程运行 如下所示 node server js 然而 2 5 小时后 终端变得不活动 进程终止 即使终端断开连接 我是否也可以使进程保持活动状态 Edit 1 事实
  • 复制目录内容

    我想将目录 tmp1 的内容复制到另一个目录 tmp2 tmp1 可能包含文件和其他目录 我想使用C C 复制tmp1的内容 包括模式 如果 tmp1 包含目录树 我想递归复制它们 最简单的解决方案是什么 我找到了一个解决方案来打开目录并读

随机推荐

  • MySQL - utf8mb4字符集设置(解决emoji表情的记录无法保存问题)

    1 emoji介绍 1 emoji 就是表情符号 来自日语词汇 絵文字 假名为 读音即 emoji 2 最早由栗田穰崇 Shigetaka Kurita 创作 并在日本网络及手机用户中流行 自苹果公司发布的 iOS 5 输入法中加入了 em
  • 医学影像的格式转换

    参考 医学影像数据之nii npz npy dcm mhd 的数据格式互转 及多目标分割处理汇总 nii gz 和mhd 钱多多先森的博客 CSDN博客
  • Python手势识别

    这是借鉴了github上的一个源程序 参考源 https github com lzane Fingers Detection using OpenCV and Python 自己在这个基础上做了一点修改补充后 可以实现手指指尖的检测 并且
  • kettle ORA-00942: 表或视图不存在(2022/01/15)

    问题描述 kettle对接客户的oracle数据库 一开始给我的账号少赋了一张表的权限 这张表是之后赋给我的 对接中发现 在kettle预览有这张表的sql的时候 就会报上述错误 但是在navicat里面跑sql是成功的 解决方案 用模式
  • FMC141-4路 250Msps/16bits ADC, FMC板卡

    FMC141 4路 250Msps 16bits ADC FMC板卡 一 产品概述 本板卡基于 FMC 标准板卡 实现 4 路 16 bit 250Msps ADC 功能 遵循 VITA 57 标准 板卡可以直接与VME VXS AMC V
  • 数据中台元年,企业数字化转型面临的三大挑战

    随着企业信息化程度越来越高 企业掌握的数据量从原来的TB级发展到PB级 再到EB级甚至往ZB级别发展 数据形式也在从原来的结构化数据为主转变为以日志 视频 图片 语音等非结构化数据为主 然而 数据存储和计算 数据组织的运行都是有成本的 当数
  • python计算GPA,附带详细讲解

    这里采用标准计算公式 S 学科分数 P 学科绩点 n 学科数目 下面举个例子 比如 数学 90 绩点4 语文 80 绩点3 英语 70 绩点2 GPA 90 4 80 3 80 2 4 4 3 2 100 3 29 相关代码如下 score
  • adb常用命令

    查看当前连接设备 adb devices如果发现多个设备 adb s 设备号 其他指令 举例 adb s devicel install xxx apk 查看顶部Activity windows环境下 adb shell dumpsys a
  • SIGSEGV与SIGBUS

    http www cnblogs com whyandinside archive 2013 01 07 2848806 html SIGSEGV与SIGBUS SIGBUS Bus error 意味着指针所对应的地址是有效地址 但总线不能
  • CMakeLists.txt 的阅读

    前言 CMake允许开发者编写一种平台无关的 CMakeList txt 文件来定制整个编译流程 然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件 如 Unix 的 Makefile 或 Windows 的 Vi
  • 【Proteus仿真】【51单片机】蔬菜大棚温湿度控制系统设计

    文章目录 一 主要功能 二 使用步骤 三 硬件资源 四 软件设计 1 主要代码 五 实验现象 联系作者 一 主要功能 1 温湿度上下限阈值设定 2 超限声光报警加热 加湿 3 温湿度显示 二 使用步骤 系统运行后 LCD1602显示当前温湿
  • Nacos快速入门(三):Spring Cloud Alibaba Nacos实现服务注册与发现

    1 前言 Spring Cloud Alibaba 是阿里巴巴提供的微服务开发一站式解决方案 目前已经加入Spring Cloud项目 跟随Spring Cloud一起维护 集成Nacos需要使用Spring Cloud Alibaba N
  • JS parseInt() 、parseFloat()、Number()

    介绍 parseInt string radix parseFloat string Number object 举例 总结 parseInt string radix parseFloat string Number object 介绍
  • c语言中宏定义的关键字,C语言中,宏定义“define”的一种有趣的用法

    在C语言中 宏定义也是一种很重要的概念 可以使用 define 来定义一个常量 也可以用 define 来实现一些功能 这一篇文章中 我们将介绍一下 在C语言中 使用宏定义 define 实现的一个小的例子 我们可以借助 define 将很
  • 汇编:按给定格式在屏幕中间显示字符串

    代码如下 assume cs code ds data ss stac data segment db welcome to masm db 02h 24h 71h data ends stac segment dw 0 0 0 0 0 0
  • Lion:闭源大语言模型的对抗蒸馏

    Lion 闭源大语言模型的对抗蒸馏 Lion 由香港科技大学提出的针对闭源大语言模型的对抗蒸馏框架 成功将 ChatGPT 的知识转移到了参数量 7B的 LLaMA 模型 命名为 Lion 在只有 70k训练数据的情况下 实现了近 95 的
  • 两台服务器文件镜像,两台云服务器镜像

    两台云服务器镜像 内容精选 换一换 请您在购买前确保已完成注册和充值 详细操作请参见如何注册公有云管理控制台的用户 登录管理控制台 单击管理控制台左上角的 选择区域和项目 选择 计算 gt 弹性云服务器 单击 购买弹性云服务器 系统进入购买
  • 删除单链表中的指定节点

    题目1 编写在带头结点的单链表L中删除一个最小值结点的高效算法 假设最小值结点是唯一的 时间复杂度为O n 空间复杂度为O 1 问题解答 算法思想 用p从头至尾扫描单链表 pre指向 p结点的前驱 用minp保存值最小的结点指针 初值为p
  • 那些方法可以绕过服务器对文件内容的检测,文件上传漏洞

    文件上传 文件上传就是在一些web应用中允许用户上传图片 文本等相应文件到服务器指定的位置 而文件上传漏洞就是利用这些可以上传的地方将恶意代码植入到服务器中 之后通过url去访问以执行代码达到攻击的目的 可以成功攻击的条件 1 存放上传文件
  • Linux网络编程:IO多路复用——epoll

    服务器端代码 epoll event poll include