epoll接口使用 -- 非阻塞式网络io(仅读事件)

news2024/11/19 15:14:10

目录

epoll接口使用

思路 

注意点

代码

封装epoll接口

epoll.sever.hpp

运行结果 


epoll接口使用

接口+epoll原理介绍 -- epoll接口介绍,epoll模型介绍+原理,接口和模型的关系,epoll优点(和select/poll进行对比)-CSDN博客

思路 

我们可以先将系统提供的epoll简单封装一下,更方便我们使用

  • 比如在对象被创建时,就创建epoll模型 ; 在对象被销毁时,自动关闭模型对应文件
  • 每次在操作红黑树结点时,不需要我们手动传入struct epoll_event结构,而是在类内创建,只 ; 并且细分操作类型,在执行删除时就不用创建了
  • 加入打印日志

虽然epoll原理上和select/poll截然不同,但在代码逻辑上是类似的

  • 监听套接字创建好后,就将它加入红黑树
  • 每次循环检测并获取[已就绪的文件及其事件]
  • 根据fd,将读事件分为两类 -- 新连接到来,读取数据就绪,这样可以将事件派发给相应的处理器
  • 处理还是老一套,和select,epoll是一样的,直接获取新连接/读取数据即可

注意点

将[通信用套接字文件]从红黑树移除时,需要调用epoll_ctl

  • 但注意,在调用前要保证该文件的fd是有效
  • 也就是,先删除,再关闭文件

代码

封装epoll接口

#include <sys/epoll.h>
#include <cstring>
#include <errno.h>
#include "Log.hpp"

static const int def_epoll_size = 128;

struct no_copy
{
    no_copy() {}
    ~no_copy() {}
    no_copy(const no_copy &t) = delete;
    no_copy operator=(const no_copy &t) = delete;
};

// 我们要保证这个对象唯一,因为没有必要存在多个[自定义操作epoll模型的方法],可以直接禁止拷贝/赋值,也可以继承一个无法拷贝/赋值的类
class MY_EPOLL : public no_copy
{
public:
    MY_EPOLL(int timeout)
    {
        sock_ = epoll_create(def_epoll_size);
        if (sock_ < 0)
        {
            lg(ERROR, "epoll_create error,%s\n", strerror(errno));
        }
        else
        {
            lg(DEBUG, "epoll_create success,fd: %d\n", sock_);
        }
        timeout_ = timeout;
    }
    ~MY_EPOLL()
    {
        if (sock_ >= 0)
        { // 保证在fd有效的情况下,关闭
            close(sock_);
        }
    }
    int wait(struct epoll_event *events, int max)
    {
        return epoll_wait(sock_, events, max, timeout_);
    }
    int ctl(int op, int fd, uint32_t event) // 这里我们设置成直接传入事件名,然后在内部构建epoll_event结构
    {
        int n = 0;
        if (op == EPOLL_CTL_DEL)
        {
            n = epoll_ctl(sock_, op, fd, nullptr); // 删除后,无论什么事件都无所谓了
            if (n == -1)
            {
                lg(ERROR, "EPOLL_CTL_DEL error\n");
            }
        }
        else
        {
            struct epoll_event t;
            t.events = event;
            t.data.fd = fd; // 方便用户层拿到时,可以知道是哪个fd就绪
            n = epoll_ctl(sock_, op, fd, &t);
            if (n == -1)
            {
                lg(ERROR, "EPOLL_CTL_ADD/MOD error,%s\n", strerror(errno));
            }
        }
        return n;
    }

private:
    int timeout_;
    int sock_;
};

epoll.sever.hpp

#include "Log.hpp"
#include "socket.hpp"
#include "myepoll.hpp"
#include <memory>

static const int def_port = 8080;
static const int def_timeout = 1000;
static const int def_size = 64;

class epoll_server
{
public:
    epoll_server()
        : p_listen_socket_(new MY_SOCKET), p_epoll_(new MY_EPOLL(def_timeout)) {}
    ~epoll_server()
    {
    }
    void start()
    {
        init();
        // 添加监听套接字
        int fd = p_listen_socket_->get_fd();
        p_epoll_->ctl(EPOLL_CTL_ADD, fd, EPOLLIN);

        while (true)
        {
            struct epoll_event events[def_size];
            int ret = p_epoll_->wait(events, def_size);
            if (ret > 0) // 有事件就绪
            {
                handle(ret, events);
            }
            else if (ret == 0) // 超时
            {
                continue;
            }
            else
            {
                perror("epoll_wait");
                break;
            }
        }
    }

private:
    void init()
    {
        p_listen_socket_->Socket();
        p_listen_socket_->Bind(def_port);
        p_listen_socket_->Listen();
    }
    void handle(int n, struct epoll_event *events)
    {
        int listen_sock_ = p_listen_socket_->get_fd();
        for (int i = 0; i < n; ++i) // 遍历有效事件
        {
            int fd = events[i].data.fd;     // 这里我们就可以知道是哪个文件上的事件就绪了
            if (events[i].events & EPOLLIN) // 有读事件就绪
            {
                if (fd == listen_sock_) // 获取新连接
                {
                    accepter(fd);
                }
                else // 读事件
                {
                    receiver(fd);
                }
            }
        }
    }
    void receiver(int fd)
    {
        char in_buff[1024];
        int n = read(fd, in_buff, sizeof(in_buff) - 1);
        if (n > 0)
        {
            in_buff[n - 1] = 0;
            std::cout << "get message: " << in_buff << std::endl;
        }
        else if (n == 0) // 客户端关闭连接
        {
            lg(INFO, "%d quit", fd);
            p_epoll_->ctl(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
        else
        {
            lg(ERROR, "fd: %d ,read error");
            p_epoll_->ctl(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
    }
    void accepter(int fd)
    {
        std::string clientip;
        uint16_t clientport;
        int sock = p_listen_socket_->Accept(clientip, clientport);
        if (sock == -1)
        {
            return;
        }
        else // 把新文件上的读事件加入红黑树
        {
            p_epoll_->ctl(EPOLL_CTL_ADD, sock, EPOLLIN);
            lg(INFO, "get a new link, client info@ %s:%d", clientip.c_str(), clientport);
        }
    }

private:
    std::shared_ptr<MY_SOCKET> p_listen_socket_;
    std::shared_ptr<MY_EPOLL> p_epoll_;
};

运行结果 

  • 服务器可以成功读取到客户端发来的数据

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

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

相关文章

Java 入门指南:Java 并发编程模式 —— 生产者-消费者模式

文章目录 生产者-消费者问题解决方案 生产者-消费者模式模式的核心问题基本原理生产者消费者 优点实现方式使用阻塞队列示例代码 使用 wait/notify 机制wait()notify()notifyAll()示例代码 使用 Exchanger示例代码 应用场景总结 生产者-消费者问题 生产者消费者问题是一个经典…

Java项目: 基于SpringBoot+mybatis+maven旅游管理系统(含源码+数据库+毕业论文)

一、项目简介 本项目是一套基于SpringBootmybatismaven旅游管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、…

【SqlServer】SQL Server Management Studio (SSMS) 下载、安装、配置使用及卸载——保姆级教程

超详细的 SQL Server Management Studio (SSMS) 下载、安装、连接数据库配置及卸载教程 SQL Server Management Studio (SSMS) 是微软提供的图形化管理工具&#xff0c;主要用于连接、管理和开发 SQL Server 数据库。以下是详细的 SSMS 下载、安装、连接数据库以及卸载的完整教…

CLIP:Learning Transferable Visual Models From Natural Language Supervision

论文:https://arxiv.org/abs/2103.00020 代码:https://github.com/openai/CLIP 官博:https://openai.com/index/clip/ 复现:https://github.com/mlfoundations/open_clip 基础知识 InfoNCE loss

S7-1500T分布式同步功能

1. 功能描述工控人加入PLC工业自动化精英社群 在一些实际应用中&#xff0c;会需要很多轴进行同步运行&#xff0c;如印刷机、纸尿裤生产线等。由于一个 PLC 的运动控制资源有限&#xff0c;控制轴的数量也是有限的&#xff0c;就会需要多个 PLC 间协调实现轴工艺对象的跨CPU的…

使用Cerbot---Let’s Encrypt生成免费的ssl证书,并设置自动更新证书

安装Certbot客户端 yum install certbot 获取证书 certbot certonly --webroot -w /var/www/demo.com -d demo.com 按照步骤 输入邮箱 同意条例 成功申请证书 修改对应的nginx的conf文件 server {listen 80;listen [::]:80;server_name demo.com;# 将 HTTP 请求重定向到 H…

分布式事务学习笔记(一)分布式事务问题、CAP定理、BASE理论、Seata

文章目录 1 分布式事务问题1.1 本地事务1.2 分布式事务1.3 创建分布式事务演示案例 2 理论基础2.1 CAP定理2.2 BASE理论2.3 解决分布式事务的思路2.4 Seata 1 分布式事务问题 1.1 本地事务 本地事务&#xff0c;也就是传统的单机事务&#xff0c;它必须要满足以下四个原则&am…

RabbitMQ延迟消息——DelayExchange插件

什么是死信以及死信交换机 当一个队列中的消息满足下列情况之一时&#xff0c;可以成为死信&#xff1a; 1. 消费者使用basic.reject或 basic.nack声明消费失败&#xff0c;并且消息的requeue参数设置为false 2. 消息是一个过期消息&#xff0c;超时无人消费 3. 要投递的队列消…

【JavaSE】--方法的使用

文章目录 1. 方法概念及使用1.1 什么是方法1.2 方法定义1.3 方法调用的执行过程1.4 实参和形参的关系&#xff08;重要&#xff09;1.5 没有返回值的方法 2. 方法重载2.1 方法重载概念2.2 方法签名 3. 递归3.1 递归的概念3.2 递归执行过程分析3.3 递归练习 1. 方法概念及使用 1…

解码3D数字人及AIGC产品,如何赋能医美行业全场景业务增长

9月13日&#xff0c;第六届“医美小小聚”暨医美信息与服务创新发展大会在热烈的氛围中拉开帷幕。此次盛会汇聚了医美行业的顶尖精英与前瞻者&#xff0c;他们围绕“聚焦营销&#xff0c;合规增长&#xff0c;融合共创”的主题&#xff0c;深入剖析了行业的新趋势、新机遇与新挑…

SpringBoot开发——整合SSL证书启用HTTPS协议

文章目录 1、https协议2、SpringBoot项目启用HTTPS协议过程2.1 创建SpringBoot项目2.2 准备SSL证书2.3SpringBoot设置2.4启动项目 1、https协议 网站使用的协议包括&#xff1a;http协议和https协议。http协议就是网址以http://开头的&#xff0c;https协议就是网址以https://…

http连接github远程仓库密码问题解决办法

目录 一、问题&#xff1a;使用http连接失败 二、解决办法&#xff1a;使用个人访问令牌。 1、生成访问令牌&#xff1a; 步骤 1: 登录 GitHub 步骤 2: 进入设置页面 步骤 3: 生成新的访问令牌 步骤 4: 配置访问令牌 步骤 5: 复制令牌 2. 使用访问令牌 一、问题&#…

从卷积的物理意义出发的第二种卷积计算方法

禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;电子信息前沿技术丛书&#xff09;》P78 第一&#xff0c;从物理意义理解卷积&#xff0c;为什么要卷&#xff0c;为什么要积。全名卷积和&#xff0c;先卷&#xff0c;再积&#xff0c;后和。 第二&#xff0c;这种方式计算节…

类加载机制和双亲委派

打印一个类加载器的示例。 import java.net.URL; import sun.misc.Launcher;public class TestJDKClassLoader {public static void main(String[] args) {System.out.println(String.class.getClassLoader());System.out.println(com.sun.crypto.provider.DESKeyFactory.clas…

Mysql 搭建主从复制

Docker Mysql 镜像启动命令(主库) docker run --name mysql-master -ti -d --privileged"true" -p 3306:3306 alibaba-cloud-linux-3-registry.cn-hangzhou.cr.aliyuncs.com/alinux3/mysql_optimized:20240221-8.0.32-2.3.0 mysql_keentune.sh 修改临时密码 如果您…

OpenCV 4.10 windows 上编译并上传conan

目录 一. 上传opencv 预编译包 二. 自己手动写一个测试包并上传 三. 自己写一个app, 引用包 一. 上传opencv 预编译包 1. 下载Opencv, 并用cmake 打开 打开工程之后&#xff0c;编译&#xff0c;install&#xff0c; 目录如下 2. 准备conan 包 把Debug 和 Release 分开放 3…

CTFHub技能树-密码口令-弱口令

目录 前提知识 BrupSuite爆破的四种模式详解 解题过程 通常认为容易被别人&#xff08;他们有可能对你很了解&#xff09;猜测到或被破解工具破解的口令均为弱口令。 前提知识 BrupSuite爆破的四种模式详解 四种模式分别为&#xff1a;Sniper、Battering ram、Pitchfork、…

Visual Studio 2022从外部引入dll导致的问题

这里以我学MapGIS二次开发的一个小demo为例 一、如何引入dll 1、在解决方案资源管理器中&#xff0c;有个引用的选项 2、然后右键点击添加引用 点击之后会出现如下&#xff1a; 3、点击浏览选项&#xff0c;选择想要引入dll的路径&#xff0c;这里我选择下载MapGIS 10的路径 …

[LitCTF 2024]SAS - Serializing Authentication

题目提示反序列化 源码 <?phpclass User {public $username;public $password;function __construct($username, $password) {$this->username $username;$this->password $password;}function isValid() { return $this->username admin && $this-&g…

《JavaEE进阶》----17.<Mybatis基本操作【注解XML】>

本篇博客详细讲解了&#xff1a;编写SQL语句 1.使用注释 2.使用XML 3.多表查询 前言&#xff1a; Mybatis规范中方法名不能重复&#xff0c;即便参数不同。因为每一个方法名都是有一个唯一的ID标识的。因此不能重复。 我们会将数据库相关的接口放在Mapper包下面。 对&#xff0…