Channel模块 --- 管理套接字事件

news2024/11/24 14:45:24

目录

设计思想

实现


设计思想

Channel模块是用于对一个描述符所需要监控的事件以及事件触发之后要执行的回调函数进行管理的

具体来说,它里面会保存该文件描述符所监控的事件,该文件描述符所就绪的事件,以及该描述符的各种事件的处理回调

每一个套接字都会对应一个 Channel 对象,用于对他的事件进行管理。

那么一个连接可能会触发的事件有哪些呢? 可读事件(EPOLLIN,EPOLLPRI),可写事件(EPOLLOUT),挂断事件,错误事件(EPOLLERR,连接出错)

有两个事件我们会有点不理解,就是EPOLLHUP 和 EPOLLRDHUP

EPOLLRDHUP,读关闭,接收缓冲区不会再有新数据到来了,内核不会再向接收缓冲区写数据,但是已经存在的数据还是可以被读取的

读关闭其实就是收到了对方的FIN请求,此时我们进入 CLOSE_WAIT 状态。 那么此时对方不会再发送数据给我们了,那么此时我们的做法应该是尽快将缓冲区中的数据读取并处理,然后将结果返回,不管这一轮处理之后是否还有不完整的报文,只要我们把发送缓冲区的数据发送完,我们就关闭连接

EPOLLHUP,读写都关闭。意味着我们不会再收到新数据,同时无法再向内核缓冲区中写入数据或者说无法在将数据发送到对端。 这可能是因为网络等原因,多次重发报文不可达时引起的状态。此时可以理解为连接已经关闭,但是我们还是需要手动关闭文件描述符

那么当该事件触发时,我们的做法是关闭连接。因为就算还有数据待处理,我们的应答也无法发送到对端了,这时候连接已经无意义了,所以我们直接关闭。

那么,我们可以把 EPOLLRDHUP看成是读事件,触发之后调用读方法,但是需要尽快关闭连接。 

而EPOLLHUP是挂断事件,他的做法其实就跟错误事件的做法类似,直接关闭连接。

当然其实一般情况下我们也无需太过关心EPOLLRDHUP,就把他当读事件就行了,因为触发 EPOLLRDHUP的时候,有两种情况,一种是没有触发读事件,那么说明没有新数据到来,那么缓冲区此时没有一个完整报文,我们只需要把输出缓冲区的数据发出去就可以直接关闭连接了。 另一种是读事件先触发,然后触发EPOLLRDHUP,这时候,由于读事件触发,我们会调用读事件的回调方法将缓冲区的所有完整报文都处理完,所以此时我们也是一样的,把输出缓冲区的数据全部发送出去就可以关闭连接了。

最后,我们还需要一个任意事件的回调方法。因为我们可能会启动超时连接的销毁机制,那么任意事件触发我们都需要刷新定时任务。同时,用户也可能会设置任意事件回调,那么任意事件到来时,我们也需要执行用户设置的任意回调(当然这一点我们在Channel模块不考虑,这是Connection模块该考虑的,Channel的回调方法全部都是Connection设置进来的,同时被Connection管理)。

而在 epoll 模型中,我们对一个描述符的事件管理是使用一个 uint32_t events 来管理的,后续事件就绪时,我们也是通过一个 uint32_t revents 来获取出来的,所以我们的Channel模块中除了五个回调函数,还需要两个 uint32_t的变量。 

我们需要设置的功能性接口:

开启读事件监控,判断读事件是否已经监控,取消读事件监控,开启写事件监控,判断写事件是否已经监控,取消写事件监控,关闭所有事件监控,移除监控 。 以及设置五个回调方法的接口。

注意,关闭所有事件监控是指在 epoll 模型中将所有的事件监控取消,不再监控,但是我们的文件描述符还在epoll模型的红黑树节点中。 而移除监控则是要在epoll模型中移除我们的节点,在移除节点指点,记得要先取消所有事件监控。

还有需要注意的是,其实移除所有事件监控并不是真的就不监控任何事件了,我们的文件描述符在epoll中,默认是自动开启 EPOLLERR ,ERPOLLHUP , ERPOLLRDHUP 这三个事件的监控的

还需要一个接口用于执行就绪事件,其实就是按照一定的逻辑来调用回调方法。

同时,未来我们获取到就绪的事件是在EventLoop 模块中获取,那么我们也需要一个接口用于设置就绪事件。

而我们也需要一个私有接口,因为我们的Channel模块只是对事件进行管理,真正进行监控的还是Poller模块以及EventLoop模块,我们未来会通过调用EventLoop模块的接口来对EventLoop模块中的Poller模块内的epoll模型中监控的事件做调整。 

其实我们所谓的启动和取消事件监控,无非就是对 epoll 模型中的文件描述符所监控的事件作更新,所以对事件的修改只需要一个接口就行了,因为未来调用的都是 epoll_ctl 这个接口。

class Channel
{
private:
    using EventCallBack = std::function<void()>;
    int _fd;             //文件描述符
    uint32_t _events;    //监控的事件
    uint32_t _revents;   //就绪的事件
    EventCallBack _read_cb;     //读事件回调
    EventCallBack _write_cb;    //写事件回调
    EventCallBack _error_cb;    //错误事件回调
    EventCallBack _close_cb;    //挂断事件回调    一般来说,挂断事件和错误事件的处理方式是一样的
    EventCallBack _event_cb;    //任意事件回调
    //EventLoop* _loop   //后续会添加的成员
private:
    //私有接口,用于真正和 Poller 模块和 EventLoop 模块联动,进行事件监控的调整
    void UpdateEvents();  
public:
    //启动读事件监控
    void EnableRead();
    //是否启动了读事件
    bool HasRead();
    //取消读事件监控
    void DisableRead();
    //启动写事件监控
    void EnableWrite();
    //是否启动了写事件
    bool HasWrite();
    //取消写事件监控
    void DisableWwrite();
    //取消所有事件监控
    void DisableAll();
    //移除监控
    void Remove();
    //设置就绪事件
    void SetRevents();
    //处理就绪事件
    void HandlerEvents();
    //设置读事件回调
    void SetReadCallBack();
    //设置写事件回调
    void SetWriteCallBack();
    //设置错误事件回调
    void SetErrorCallBack();
    //设置挂断事件回调
    void SetCloseCallBack();
    //设置任意事件回调
    void SetEventCallBack();
};

当然,后续我们实现了EventLoop模块之后,我们会在Channel模块中再添加一个成员,就是 EventLoop* loop ,因为我们对事件的实际操作是需要通过 EventLoop 的 接口来完成的。

实现

    //私有接口,用于真正和 Poller 模块和 EventLoop 模块联动,进行事件监控的调整
    void UpdateEvents(int op)  //op 就是未来传递给 epoll_ctl 的op参数
    {
        //后续调用EventLoop提供的接口
        //_loop->UpdateEvents(_fd,op,_events);
    }

简单接口实现 


    Channel(int fd):_fd(fd),_events(0),_revents(0){}
    //启动读事件监控
    void EnableRead()
    {
        if(HasRead())  return ;//说明已经监控了读事件了 
        _events|= EPOLLIN;
        UpdateEvents(EPOLL_CTL_MOD); 
    }
    //是否启动了读事件
    bool HasRead()
    {
        return _events & EPOLLIN;
    }
    //取消读事件监控
    void DisableRead()
    {
        if(!HasRead()) return ; //说明本来就没有监控读事件
        _events &= (~EPOLLIN);
        UpdateEvents(EPOLL_CTL_MOD);
    }
    //启动写事件监控
    void EnableWrite()
    {
        if(HasWrite()) return ;
        _events |= EPOLLOUT;
        UpdateEvents(EPOLL_CTL_MOD);
    }
    //是否启动了写事件
    bool HasWrite()
    {
        return _events & EPOLLOUT;
    }
    //取消写事件监控
    void DisableWwrite()
    {
        if(!HasWrite()) return;
        _events &= (~EPOLLOUT);
        UpdateEvents(EPOLL_CTL_MOD);
    }
    //取消所有事件监控
    void DisableAll(){_events = 0;}
    //移除监控
    void Remove(){UpdateEvents(EPOLL_CTL_DEL);}
    //设置就绪事件
    void SetRevents(uint32_t revents){_revents = revents;}

稍微复杂一点的接口就是处理就绪事件的接口。在处理就绪事件的时候,我们需要判断哪些事件就绪了。 同时,我们要注意一个点:

读事件中间出错,我们是不会直接关闭连接的,因为可能还有数据未发送出去。

但是如果写事件出错,那么我们是直接关闭连接了,因为后续就算再处理数据也没用了。 

而如果挂断事件和错误事件,我们也是关闭连接。

而如果发生了挂断事件或者错误事件,那么一定会在写事件中体现出来,但是关闭连接的操作我们只需要进行一次,所以我们在处理事件的时候,写事件,挂断事件和错误事件只需要处理一种就行了,如果连接有问题,那么在任意一个事件的处理中就会关闭连接。

    //处理就绪事件
    void HandlerEvents()
    {
        //读事件需要处理
        if(_revents & (EPOLLIN | EPOLLPRI | EPOLLRDHUP))  
        {
            if(_read_cb) _read_cb();
            if(_event_cb) _event_cb(); //任意事件触发都需要调用,防止事件
        }
        //剩下三个事件只需要处理其中一种
        if(_revents & EPOLLOUT)
        {
            if(_event_cb) _event_cb();  //因为写事件可能会关闭连接,所以没办法,我们只能放在前面调用了
            if(_write_cb) _write_cb();
        }
        else if(_revents & EPOLLHUP)
        {
            if(_event_cb) _event_cb();
            if(_close_cb) _close_cb();
        }
        else if(_revents & EPOLLERR)
        {
            if(_event_cb) _event_cb();
            if(_close_cb) _close_cb();
        }
        return;
    }

剩下四个接口就是最简单的设置回调函数的接口了。

    //设置读事件回调
    void SetReadCallBack(const EventCallBack& cb){ _read_cb = cb;}
    //设置写事件回调
    void SetWriteCallBack(const EventCallBack& cb){ _write_cb = cb;}
    //设置错误事件回调
    void SetErrorCallBack(const EventCallBack& cb){ _error_cb = cb;}
    //设置挂断事件回调
    void SetCloseCallBack(const EventCallBack& cb){ _close_cb = cb;}
    //设置任意事件回调
    void SetEventCallBack(const EventCallBack& cb){ _event_cb = cb;}

那么Channel类就完成了,由于Channel类不好单独测试,所以这份代码我们只进行编译纠错,等到后续写完Poller模块我们再进行联合调试。

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

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

相关文章

4 机器学习之归纳偏好

通过学习得到的模型对应了假设空间中的一个假设。于是&#xff0c;图1.2的西瓜版本空间给我们带来一个麻烦&#xff1a;现在有三个与训练集一致的假设&#xff0c;但与它们对应的模型在面临新样本的时候&#xff0c;却会产生不同的输出。例如&#xff0c;对&#xff08;色泽青绿…

java算法oj(3)栈和队列

目录 1.前言 2.正文 2.1基础操作 2.2用栈实现队列 2.2.1题目 2.2.2示例 2.2.3代码 2.3用队列实现栈 2.3.1题目 2.3.2示例 2.3.3代码 2.4最小栈 2.4.1题目 2.4.2示例 2.4.3代码 3.小结 1.前言 哈喽大家好吖&#xff0c;今天来分享几道栈与队列的算法题&#…

三、AOP

文章目录 1. AOP&#xff08;概念&#xff09;2. AOP&#xff08;底层原理&#xff09;2.1 AOP 底层使用动态代理2.2 AOP&#xff08;JDK动态代理&#xff09; 3. AOP&#xff08;术语&#xff09;3.1 连接点3.2 切入点3.3 通知&#xff08;增强&#xff09;3.4 切面 4. AOP操作…

数据丢失不再怕!四款数磁盘数据恢复据恢复工具实测心得

在办公室行政的工作中&#xff0c;数据恢复工具是我们不可或缺的帮手。无论是不小心删除了重要文件&#xff0c;还是硬盘突然罢工&#xff0c;这些工具总能在关键时刻帮我们力挽狂澜。今天&#xff0c;我就来分享一下我使用福昕数据恢复、转转大师数据恢复、超级兔子数据恢复和…

MySQL 【数字】函数大全(二)

MODPIPOWPOWERRANDROUNDSIGNSQRTTRUNCATE 1、MOD MOD(number1, number2) &#xff1a;返回一个数字除以另一个数字的余数。 语法&#xff1a; 1、MOD(number1, number2) 2、number1 MOD number2 3、number1 % number2 number1&#xff1a;被除数。 number2&#xff1a;除数。…

Qt学习系列之设计模式的小记录

Qt学习系列之设计模式的小记录 前言Qt中的设计模式使用情况数据模型视图组建代理 小tips虚函数&#xff1a;基类的不同具体使用界面设计后台显示 报错解决 总结 前言 在软件设计师中的设计模式有提到设计模式有三种类型&#xff1a; 创建型&#xff1a;工厂方法模式、抽象工厂…

上海AI实验室CVT-Occ时间融合利用视差搜索刷新occ3DWaymo SOTA

上海AI实验室提出CVT-Occ&#xff1a;通过时间融合利用视差搜索刷新occ3D-Waymo SOTA Abstract 基于视觉的3D占据预测由于单目视觉在深度估计上的固有局限性而面临显著挑战。本文介绍了CVT-Occ&#xff0c;一种新颖的方法&#xff0c;通过时间上的体素几何对应进行时间融合&a…

华为OD机试 - 最快到达医院的方法(Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

欧洲气象局开源数据如何通过大模型预测未来天气

气象预测依赖于全球各地的观测数据、复杂的数学模型和高性能计算资源。欧洲气象局&#xff08;ECMWF&#xff0c;European Centre for Medium-Range Weather Forecasts&#xff09;作为世界领先的气象预报机构&#xff0c;不仅提供精确的天气预报服务&#xff0c;还向公众和科研…

获取鸿蒙设备Udid遇到的问题

参考官方文档&#xff1a;注册调试设备-调试应用&#xff08;HarmonyOS&#xff09;-AppGallery Connect帮助中心 - 华为HarmonyOS开发者 (huawei.com) 坑一&#xff1a;The sdk hdc.exe version is too low, please upgrade to the latest version. 升级dev工具和sdk配置为api…

google域名邮箱:如何创建并管理邮箱账户?

google域名邮箱设置教程&#xff1f;如何开通Google域名邮箱&#xff1f; Google域名邮箱提供了一个专业且功能强大的平台&#xff0c;允许用户使用自定义域名创建和管理邮箱账户。烽火将详细介绍如何创建并管理Google域名邮箱账户&#xff0c;帮助您充分利用这一强大的工具。…

Java Mail腾讯企业邮箱或其他邮箱发送邮件失败bug记录

问题出现情况 邮件发送时debug用F8逐步运行可以成功发送邮件&#xff0c;但是用F9或者直接运行程序却发送失败未开启mail的debug模式的报错日志是下面这个&#xff1a;org.springframework.mail.MailAuthenticationException: Authentication failed; nested exception is java…

基于RPA+AI的网页自动填写机器人 | OPENAIGC开发者大赛高校组优秀作品

在第二届拯救者杯OPENAIGC开发者大赛中&#xff0c;涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到&#xff0c;我们特意开设了优秀作品报道专栏&#xff0c;旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者&#xff0c;希望能带给…

TXS0104 LSF0102 原理理解

具体参数请查看数据手册&#xff0c;在此不必赘述了。TXS0104 数据手册 现主要对TXS0104 结构原理进行解析&#xff1a; 电平转换电路结构可参考如下电路&#xff1a; 对于gate bias 部分电路具体细节有兴趣的可以了解下。 对于LSF0102 系列的电平转换芯片 LSF0102 数据手…

【人工智能学习之PaddleOCR训练教程】

具体的训练教程&#xff1a; 准备数据集 OCR数据集 &#xff08;以下文字转载官方文档ocr_datasets&#xff09; 1. 文本检测 1.1 PaddleOCR 文字检测数据格式1.2 公开数据集 1.2.1 ICDAR 2015 2. 文本识别 2.1 PaddleOCR 文字识别数据格式2.2 公开数据集 2.1 ICDAR 2015 …

使用Spring Security实现用户-角色-资源的权限控制

文章目录 一、基于角色的请求控制二、加载用户角色信息三、角色与资源的关联四、测试角色权限控制1. 未登录用户访问受保护资源2. 登录用户访问受保护资源3. 角色不足的用户访问受保护资源&#xff08;把前面改成.roles("USER")&#xff09; 五、自定义异常处理1. 自…

CANoe的数据回放功能

文章目录 CANoe简介1.Offline回放功能2.Replay Block回放模块3.两者的异同 CANoe简介 CANoe&#xff08;CAN open environment&#xff09;&#xff1a; 用于汽车ECU网络仿真、数据分析、测试和诊断&#xff0c;还可以进行数据回放、程序刷写等。具有实时报文跟踪、数据解析及…

Android开发启动页跳过倒计时效果

Android开发启动页跳过倒计时效果 app启动页都会放些广告图&#xff0c;然后倒计时跳过。 一、思路&#xff1a; 不是很难&#xff0c;就用用个倒计时控制mHandler.sendEmptyMessageDelayed(WHAT_COUNT_DOWN, 100); 二、效果图&#xff1a; 三、关键代码&#xff1a; publ…

消息队列面试题——第二篇

1. rocketmq、rabbitmq、kafka的区别 架构设计和消息模型 特性rocketmqrabbitmqkafka消息模型基于主题和消费组&#xff0c;支持发布/订阅和点对点两种模型基于队列模型&#xff0c;支持发布/订阅和点对点两种模型基于分区的主题模型&#xff0c;主要用于日志流式处理和高吞吐…

基于SSM创城志愿者管理系统JAVA|VUE|Springboot计算机毕业设计源代码+数据库+LW文档+开题报告+答辩稿+部署教+代码讲解

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿 部署教程代码讲解代码时间修改教程 一、开发工具、运行环境、开发技术 开发工具 1、操作系统&#xff1a;Window操作系统 2、开发工具&#xff1a;IntelliJ IDEA或者Eclipse 3、数据库存储&#xff1a…