【计网】实现reactor反应堆模型 --- 处理数据发回问题 ,异常处理问题

在这里插入图片描述

没有一颗星,
会因为追求梦想而受伤,
当你真心渴望某样东西时,
整个宇宙都会来帮忙。
--- 保罗・戈埃罗 《牧羊少年奇幻之旅》---

实现Reactor反应堆模型

  • 1 数据处理
  • 2 数据发回问题
  • 3 异常处理问题
  • 4 运行效果

1 数据处理

在上一篇文章中我们搭建起了Reactor反应堆模型的基础框架,可以实现对数据接受。那么接下来就需要对数据进行处理之后发回。
在这里插入图片描述

数据处理方面,需要使用到协议,我这里采取使用JSON串的形式,将之前网络计算器项目的协议结构直接拿过来使用。
【计网】从零开始掌握序列化 — 实现网络计算器项目

HandlerConnection模块中connection连接的缓冲区中会接受到新的数据。接收到数据之后HandlerConnection模块的工作就完成了,下面需要进入协议解析模块进行处理。直接调用HandlerConnection模块的回调函数_process,进入协议解析模块。

协议解析模块的逻辑很简单:

  1. 判断缓冲区中是否有完整报文
  2. 对完整报文进行协议解析
  3. 对解析出来的数据进行处理,得到应答报文
  4. 将应答报文发送回去
#pragma once#include "Connection.hpp"
#include "InetAddr.hpp"
#include "Protocol.hpp"
#include "Log.hpp"
#include "NetCal.hpp"using namespace log_ns;class PackageParse
{
public:void Execute(Connection *conn){LOG(INFO, "service start!!!\n");while (true){// 1.报文解析std::string str = Decode(conn->Inbuffer()); // 通过去报头获取报文// std::cout << "str: " << str << std::endl;// 连接当前没有完整的报文! --- 直接退出if (str.empty())break;// 到这里说明有完整的报文!!!auto req = Factory::BuildRequestDefault();// 2.反序列化 得到 Requestreq->Deserialize(str);// auto res = Factory::BuildResponseDefault();// 3.业务处理auto res = cal.Calculator(req);// 4.进行序列化处理std::string ret;res->Serialize(&ret);std::cout << "ret: " << ret << std::endl;// 5.加入报头std::string package = Encode(ret);//std::cout << "package: \n"<< package << std::endl;// 6.发回数据// 直接进行发送 , 怎么知道写入条件不满足呢? 通过错误码errno是EAGAIN即可。conn->AppendOutbuffer(package);}// 到了这里 说明至少处理了一个请求 只是有一个应答// 进行发回数据if (!conn->Outbuffer().empty())conn->_handler_sender(conn); // 方法1:直接发回数据// 方法2:将写事件设置进入EPOLL就可以了 会自动进行读取}~PackageParse(){}private:NetCal cal; // 计算器
};

这里处理结束后,要将数据发回,但是我们还没有实现数据发回的逻辑,接下来我们来分析一下发回数据要怎么处理

2 数据发回问题

对于多进程与多线程的情况下,write更加简单,有多少发多少,直接进行阻塞式写入

但是对于多路转接来说,write比较复杂:

  • 当我们获得一个新的的fd时, 输入输出缓冲区默认都是空的
  • 读事件就绪:本质就是输入缓冲区有了数据,有了新连接。
  • 写事件就绪:不关心数据是什么,而是关心发送缓冲区中有没有空间,如果有空间,发送条件就是就绪的,否则不满足。
  • 把一个sockfd 托管给 select poll epoll,原因sockfd上事件没有就绪,还是事件就绪了?当然是不就绪的时候托管给EPOLL
  • 默认sockfd新建的情况下,读事件不是就绪的,因为输入缓冲区没有数据,所以读事件要常添加到epoll中托管
  • 默认sockfd新建的情况下,写事件是就绪的,因为输出缓冲区没有数据,所以写事件默认是直接写的
  • 所以 只有当写入条件不满足时,我们才按需开启对sockfd的EPOLLOUT事件进行托管, 一直写,到缓冲区写满时数据还没有发完,就需要开启对写事件的关心!
  • 对于写来说,当写入时出现条件不满足的情况时,后续剩余的数据,EPOLL会自动进行发送!
  • 如果直接对一个sockfd设置EPOLLOUT关心,epoll就会大量的就绪,因为输出缓冲区不会第一时间写满!
    未来如果发完了,对于EPOLLOUT事件的关心,就要被关闭
    如果缓冲区没写满,数据也发完了 ,就不需要开启写事件关心
    如果我们设置了对EPOLLOUT的关心,EPOLL对EPOLLOUT首次设置关心的时候默认会就绪一次!

这是根据以上准则整理出的代码:

void Sender(Connection *conn){errno = 0;//进行发送while(true){//进行发送数据ssize_t n = ::send(conn->Sockfd() ,conn->Outbuffer().c_str() , conn->Outbuffer().size() , 0);//发送成功if(n > 0){//向将读取成功的数据从缓冲区删除conn->DiscardOutbuffer(n);//判断是否读取完if(conn->Outbuffer().empty()) {break ;}}else if(n == 0){break ;}else{//通过errno判断错误类型if(errno == EWOULDBLOCK){//说明输出缓冲区满了break;}else if(errno == EINTR){//信号中断 继续发送continue;}else{//真的出错了LOG(ERROR , "send error , errno:%d\n" ,errno);//进入异常处理conn->_handler_excepter(conn);return ;}}}//到这里说明是写入条件不满足了if(!conn->Outbuffer().empty()){//EPOLL进行托管//开启对写事件的关心//LOG(DEBUG , "----------\n");conn->GetReactor()->EnableConnectionReadWrite(conn->Sockfd() , true , true); //发送完了呢?}else{//将写事件关闭conn->GetReactor()->EnableConnectionReadWrite(conn->Sockfd(), true , false);}}

这样我们就可以成功的将数据发回,并在发回条件不满足时,将写事件托管给EPOLL进行自动发送!发完之后需要将EPOLLOUT事件撤销。

这样写入的问题就解决了

3 异常处理问题

在写入和读取数据的过程中,所有的异常我们都是交给异常处理方法进行解决。而所以的异常,最终的都是要将连接中断,文件描述符关闭,解除EPOLL托管。

    void Excepter(Connection *conn){//整个代码所以的逻辑异常 都在这里处理//删除连接conn->GetReactor()->DelConnection(conn->Sockfd());}

去除连接的代码在Reactor中:

	void DelConnection(int sockfd){// 安全检测if (!IsConnExist(sockfd))return;LOG(INFO, "sockfd: %d quit , 服务器释放资源\n", sockfd);// 在内核中移除sockfd关心EnableConnectionReadWrite(sockfd, false, false);_epoller->DelEvent(sockfd);// Socketfd要进行关闭_conn[sockfd]->Close();// 在Reactor中移除Connection的关心delete _conn[sockfd];_conn.erase(sockfd);}

经过这个处理,出现异常的连接,就直接别删除了。

4 运行效果

截止目前为止,我们已经实现了:

  1. 通过Reactor托管Listener获取新连接
  2. EPOLL对新连接的读事件进行托管,获取数据
  3. 得到数据之后可以进行上层的协议解析与业务处理
  4. 数据处理之后,可以进行发回数据,发回条件不满足时,可以将写事件托管给Reactor进行自动处理

来看效果:
在这里插入图片描述

效果非常可以了!

下一篇文章我们来解决如何加入多线程与多进程,提高效率!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Science Robotics 综述揭示演化研究新范式,从机器人复活远古生物!

在地球46亿年的漫长历史长河中&#xff0c;生命的演化过程充满着未解之谜。如何从零散的化石证据中还原古生物的真实面貌&#xff1f;如何理解关键演化节点的具体过程&#xff1f;10月23日&#xff0c;Science Robotics发表重磅综述&#xff0c;首次系统性提出"古生物启发…

string接口的深度理解(内附思维导图)

1. 为什么学习string类&#xff1f; C语言中的字符串 C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列 的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP的思想&#xff0…

python可视化将多张图整合到一起(画布)

这周有点事忙着&#xff0c;没时间重温刚结束的Mathurcup数学建模&#xff0c;这两天也是再看了下&#xff0c;论文还是赶紧挺烂的&#xff0c;但比国赛又有进步&#xff08;说起国赛又不得不抱怨了&#xff0c;基本其余省份都发了&#xff0c;但江西......哎&#xff09;。哎&…

jsp+sevlet+mysql实现用户登陆和增删改查功能

jspsevletmysql实现用户登陆和增删改查功能 一、系统介绍二、功能展示1.用户登陆2.用户列表3.查询用户信息4.添加用户信息5.修改用户信息6.删除用户信息 四、其它1.其他系统实现 一、系统介绍 系统主要功能&#xff1a; 用户登陆、添加用户、查询用户、修改用户、删除用户 二…

Python小白学习教程从入门到入坑------第二十九课 访问模式文件定位操作(语法进阶)

一、访问模式 模式可做操作若文件不存在是否覆盖r只能读报错-r可读可写报错是w只能写创建是w可读可写创建是a只能写创建否&#xff0c;追加写a可读可写创建否&#xff0c;追加写 1.1 r r&#xff1a;只读模式(默认模式)&#xff0c;文件必须存在&#xff0c;不存在就会报错…

TIOBE 编程指数 11 月排行榜公布 VB.Net第九

IT之家 11 月 9 日消息&#xff0c;TIOBE 编程社区指数是一个衡量编程语言受欢迎程度的指标&#xff0c;评判的依据来自世界范围内的工程师、课程、供应商及搜索引擎&#xff0c;今天 TIOBE 官网公布了 2024 年 11 月的编程语言排行榜&#xff0c;IT之家整理如下&#xff1a; P…

聚合联盟的优势

聚合广告联盟对比其他平台优势&#xff1a; 数据透明&#xff0c;自己去平台查看不存在扣量问题。对OVHM做策略优化&#xff0c;帮助开发者做多重点击和下载&#xff0c;使开发者利益最大化。为开发者提供app各大市场上架&#xff0c;隐私协议等指导。 最大的优势就是数据公开…

FakeLocation 版本问题

前言:最新版的FakeLocation 1.3.5 BETA版本在appconfigs.xml文件种添加了绝大多数的应用,导致会返回真实的物理位置&#xff0c;在1.3.2.2都没有这个问题&#xff0c;但是旧版是会被强制更新&#xff0c;不然无法使用. 版本问题/注入/代理 方法 需要使用FakeLocation有二种办法…

算法(第一周)

一周周五&#xff0c;总结一下本周的算法学习&#xff0c;从本周开始重新学习许久未见的算法&#xff0c;当然不同于大一时使用的 C 语言以及做过的简单题&#xff0c;现在是每天一题 C 和 JavaScript&#xff08;还在学&#xff0c;目前只写了一题&#xff09; 题单是代码随想…

大语言模型LLMs在医学领域的最新进展总结

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 相比其他学科&#xff0c;医学AI&#xff0c;是发表学术成果最多的领域。 医学数据的多样性和复杂性&#xff08;包括文本、图像、基因组数据等&#xff09;&#xff0c;使得…

服务器被病毒入侵如何彻底清除?

当服务器遭遇病毒入侵时&#xff0c;彻底清除病毒是确保系统安全和数据完整性的关键步骤。这一过程不仅需要技术上的精准操作&#xff0c;还需要严密的计划、合理的资源调配以及后续的防范措施。以下是一篇关于如何在服务器被病毒入侵时彻底清除病毒的详细指南。 一、初步响应与…

Javascript中如何实现函数缓存?函数缓存有哪些应用场景?

#一、是什么 函数缓存&#xff0c;就是将函数运算过的结果进行缓存 本质上就是用空间&#xff08;缓存存储&#xff09;换时间&#xff08;计算过程&#xff09; 常用于缓存数据计算结果和缓存对象 解释 const add (a,b) > ab; const calc memoize(add); // 函数缓存…

基于LLaMA-Factory微调Llama3

本文简要介绍下基于LLaMA-Factory的llama3 8B模型的微调过程 环境配置 # 1. 安装py3.10虚拟环境 conda create -n py3.10-torch2.2 python3.10 source activate conda activate py3.10-torch2.2# 2. 安装cuda12.2 gpu版torch2.2 conda install pytorch2.2.2 torchvision0.17.…

学习记录:js算法(九十):N皇后

文章目录 N 皇后思路一 N 皇后 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇…

RTC精度及校准

RTC精度偏差&#xff1a; RTC的基准时间和精度与石英晶体的频率相关&#xff0c;晶体的谐振频率取决于温度&#xff0c;因此RTC性能与温度相关&#xff0c;晶体的频率偏差是晶体正常频率的温度反转函数。 一、硬件方面&#xff1a; 1.使用高精度振荡器的RTC模块&#xff1b; …

Axure PR 9 多级下拉选择器 设计交互

​ 大家好&#xff0c;我是大明同学。 Axure选择器是一种在交互设计中常用的组件&#xff0c;这期内容&#xff0c;我们来探讨Axure中多级下拉选择器设计与交互技巧。 下拉列表选择输入框元件 创建选择输入框所需的元件 1.在元件库中拖出一个矩形元件。 2.选中矩形元件&…

【设计模式系列】享元模式(十五)

目录 一、什么是享元模式 二、享元模式的角色 三、享元模式的典型应用场景 四、享元模式在ThreadPoolExecutor中的应用 1. 享元对象&#xff08;Flyweight&#xff09;- 工作线程&#xff08;Worker&#xff09; 2. 享元工厂&#xff08;Flyweight Factory&#xff09;- …

LeetCode热题100之贪心算法

1.买卖股票的最佳时机 思路分析&#xff1a;即需要找出某一天的最低价格和它后面几天的最高价格差。 维护一个变量min_price&#xff0c;表示到目前为止遇到的最低股票价格&#xff1b;遍历prices数组&#xff0c;在每一天的价格上&#xff1a; 更新min_price为当前的价格和mi…

git 对已提交的说明进行编辑

如果提交代码的时候&#xff0c;对上次提交代码的说明不准确的话&#xff0c;例如 1、可以使用 git log 查看代码提交的记录&#xff1b; 2、使用 git commit --amend 命令对上次提交的说明进行编辑&#xff1a; 当显示上次提交的内容的时候&#xff0c;按下键盘 i 键即可编辑…

Hive简介 | 体系结构

Hive简介 Hive 是一个框架&#xff0c;可以通过编写sql的方式&#xff0c;自动的编译为MR任务的一个工具。 在这个世界上&#xff0c;会写SQL的人远远大于会写java代码的人&#xff0c;所以假如可以将MR通过sql实现&#xff0c;这个将是一个巨大的市场&#xff0c;FaceBook就这…