Zinx框架的高级用法

一、使用框架提供的实用类

zinx框架已经提供了常用的IO通道类-TCP。

阅读Tcp相关类的使用文档,将之前的3个案例用TCP的方式实现。

步骤:

  1. 创建Tcp数据通道类继承ZinxTcpData,重写GetInputNextStage函数,内容跟之前标准输入通道类的内容完全相同,但不直接构造对象。

  2. 创建Tcp工厂类,重写CreateTcpDataChannel函数,只构造一个Tcp数据通道对象,返回对象指针

  3. 创建ZinxTCPListen对象,指定好监听端口号和工厂对象。并将其添加到kernel中。

#include <zinx.h>
#include <ZinxTCP.h>
#include <iostream>
using namespace std;

/*define class used to write stdout*/
class TestStdout:public Ichannel{
public:
    /*do nothing*/
    virtual bool Init(){}
    /*do nothing*/
    virtual bool ReadFd(std::string &_input){return false;}
    /*write to STDOUT directly*/
    virtual bool WriteFd(std::string &_output){
        cout << _output <<endl;
        return true;
    }
    /*do nothing*/
    virtual void Fini(){}
    /*return 1 which point to STDOUT*/
    virtual int GetFd(){return 1;}
    /*no impact*/
    virtual std::string GetChannelInfo(){return "test";}
    /*no next stage*/
    virtual AZinxHandler *GetInputNextStage(BytesMsg &_oInput){return NULL;}
} *poOut = new TestStdout();

class Echo:public AZinxHandler
{
public:
    /*define echo action which is get string from input, and send out it via stdout channel object*/
    virtual IZinxMsg *InternelHandle(IZinxMsg &_oInput){
        GET_REF2DATA(BytesMsg, oBytes, _oInput);
        auto pchannel = ZinxKernel::Zinx_GetChannel_ByInfo(oBytes.szInfo);

        if (NULL != pchannel)
        {
            ZinxKernel::Zinx_SendOut(oBytes.szData, *pchannel);
        }
        return NULL;
    }
    /*no next stage*/
    virtual AZinxHandler *GetNextHandler(IZinxMsg &_oNextMsg){return NULL;}
} *poEcho = new Echo();


class ExitFramework:public AZinxHandler
{
public:
    virtual IZinxMsg *InternelHandle(IZinxMsg &_oInput){
        GET_REF2DATA(BytesMsg, oBytes, _oInput);
        if (oBytes.szData == "exit")
        {
            ZinxKernel::Zinx_Exit();
            return NULL;
        }
        return new BytesMsg(oBytes);
    }
    virtual AZinxHandler *GetNextHandler(IZinxMsg &_oNextMsg){return poEcho;}
} *poExit = new ExitFramework();

class CmdHandler:public AZinxHandler
{
public:
    virtual IZinxMsg *InternelHandle(IZinxMsg &_oInput){
        GET_REF2DATA(BytesMsg, oBytes, _oInput);
        if (oBytes.szData == "close")
        {
            ZinxKernel::Zinx_Del_Channel(*poOut);
            return NULL;
        }
        else if (oBytes.szData == "open")
        {
            ZinxKernel::Zinx_Add_Channel(*poOut);
            return NULL;
        }
        return new BytesMsg(oBytes);
    }
    virtual AZinxHandler *GetNextHandler(IZinxMsg &_oNextMsg){
        GET_REF2DATA(BytesMsg, oBytes, _oNextMsg);
        if (oBytes.szData == "exit")
        {
            return poExit;
        }
        else
        {
            return poEcho;
        }
    }
} *poCmd = new CmdHandler();


class TestStdin:public Ichannel{
public:
    /*do nothing*/
    virtual bool Init(){}
    virtual bool ReadFd(std::string &_input){
        cin>>_input;
        return true;
    }
    /*do nothing*/
    virtual bool WriteFd(std::string &_output){return false;}
    /*do nothing*/
    virtual void Fini(){}
    /*return 0 which point to STDIN*/
    virtual int GetFd(){return 0;}
    /*no impact*/
    virtual std::string GetChannelInfo(){return "test";}
    /*specify next stage is echo handler*/
    virtual AZinxHandler *GetInputNextStage(BytesMsg &_oInput){return poCmd;}
} *poIn = new TestStdin();

class TestTcpData:public ZinxTcpData{
public:
    TestTcpData(int _fd):ZinxTcpData(_fd){}
    virtual AZinxHandler *GetInputNextStage(BytesMsg &_oInput){return poCmd;}
};

class TestTcpFact:public IZinxTcpConnFact{
    virtual ZinxTcpData *CreateTcpDataChannel(int _fd)
    {
        return new TestTcpData(_fd);
    }
};

/*before main func called, three globle object was created before which were poOut point to a TestStdout object, poEcho point to a Echo object and poIn point to a TestStdin object.*/
int main()
{
    ZinxKernel::ZinxKernelInit();
    /*Add stdin and stdout channel to kernel*/
    ZinxKernel::Zinx_Add_Channel(*poIn);
    ZinxKernel::Zinx_Add_Channel(*poOut);
    auto tlc = new ZinxTCPListen(7766, new TestTcpFact());
    ZinxKernel::Zinx_Add_Channel(*tlc);
    /*start loop*/
    ZinxKernel::Zinx_Run();
    ZinxKernel::ZinxKernelFini();
    return 0;
}

二、编写一组实用类

需求:定时3秒钟,周期地向标准输出打印hello world

分析:

  • 怎么定时?是否可以通过fd反映超时?

  • 超时之后呢?直接输出hello world?(编写实用类要面向“客户”)

  • 定时的周期能否动态改?

思路:

  • 创建一个ZinxTimer类继承Ichannel类,这个类通过timerfd用来产生超时事件

  • 创建一个ZinxTimerDeliver类继承AZinxHandler类,这个类用来管理每次超时事件的分发和超时时间管理

  • 定义一个接口(全部方法都是纯虚函数的抽象类),提供纯虚函数用来处理超时事件

1.创建TimerOutProc抽象类

  • 仅提供两个纯虚函数,若有任务需要定时处理,则应该继承该类,重写这两个虚函数

  • Proc函数会在定时周期到期时被调用

  • GetTimerSec函数会在启动下一次定时任务时被调用,用来返回定时周期

class TimerOutProc {
public:
	virtual void Proc() = 0;
	virtual int GetTimerSec() = 0;
	virtual ~TimerOutProc();
};

2.创建ZinxTimerDeliver类

  • 需要重写的函数中最重要的是InternelHandle

  • 在InternelHandle中应该找出哪些TimerOutProc对象设定的时间到了,并执行其回调函数

  • 提供RegisterProcObject和UnRegisterProcObject函数用于注册TimerOutProc对象

  • 存储TimerOutProc对象时,要使用时间轮数据结构

  • 对于超时的管理应该是全局唯一的,所以需要单例模式

//定时器节点
struct WheelNode{
	int LastCount = -1;
	TimerOutProc *pProc = NULL;
};

class ZinxTimerDeliver :public AZinxHandler
{
	static ZinxTimerDeliver m_single;
	//当前轮转刻度
	int m_cur_index = 0;
	//时间轮向量,每个坑中放一个multimap,multmap元素是圈数和定时器节点
	std::vector<std::multimap<int, WheelNode> > m_TimerWheel;
public:
	ZinxTimerDeliver();
	static ZinxTimerDeliver &GetInstance() {
		return m_single;
	}

	bool RegisterProcObject(TimerOutProc &_proc);
	void UnRegisterProcObject(TimerOutProc &_proc);

	// 通过 AZinxHandler 继承
	virtual IZinxMsg * InternelHandle(IZinxMsg & _oInput) override;

	virtual AZinxHandler * GetNextHandler(IZinxMsg & _oNextMsg) override;

};

bool ZinxTimerDeliver::RegisterProcObject( TimerOutProc & _proc)
{
    //计算圈数
	int last_count = _proc.GetTimerSec() / m_TimerWheel.size();
    
    //计算齿数
	int index = _proc.GetTimerSec() % m_TimerWheel.size();
	index += m_cur_index;
	index %= m_TimerWheel.size();

    //创建一个定时器节点存放圈数和定时器任务
	WheelNode tmp;
	tmp.LastCount = last_count;
	tmp.pProc = &_proc;

    //将定时器节点插入时间轮
	m_TimerWheel[index].insert(pair<int, WheelNode>(last_count, tmp));

	return true;
}

void ZinxTimerDeliver::UnRegisterProcObject(TimerOutProc & _proc)
{
    //去注册就是遍历查找和删除
	for (auto single_map:m_TimerWheel)
	{
		for (auto itr = single_map.begin(); itr != single_map.end(); itr++)
		{
			if (itr->second.pProc == &_proc)
			{
				single_map.erase(itr);
				return;
			}
		}
	}
}

//处理超时的核心逻辑
IZinxMsg * ZinxTimerDeliver::InternelHandle(IZinxMsg & _oInput)
{
	uint64_t counts;
	GET_REF2DATA(BytesMsg, oBytes, _oInput);

    //获取当前超时的次数,一般是1,
	oBytes.szData.copy((char *)&counts, sizeof(counts), 0);
	for (int i = 0; i < counts; i++)
	{
        //定义list存储超时的定时器节点,方便重新插入时间轮和后续回调
		list<WheelNode> wait_proc;
		for (auto itr = m_TimerWheel[m_cur_index].begin(); itr != m_TimerWheel[m_cur_index].end();)
		{
            //遍历当前齿轮内的所有节点,将圈数减一
			itr->second.LastCount--;
			if (itr->second.LastCount <= 0)
			{
				itr->second.LastCount = itr->first;
				wait_proc.push_back(itr->second);
                //删掉已经超时的定时器节点
				itr = m_TimerWheel[m_cur_index].erase(itr);
			}
			else
			{
				itr++;
			}
		}
		for (auto task : wait_proc)
		{
            //调用超时处理函数
			task.pProc->Proc();
            //将本次遍历超时的所有定时器节点重新添加到时间轮中
			RegisterProcObject(*(task.pProc));
		}
        //刻度加一
		m_cur_index++;
        //刻度超了则转回来
		m_cur_index %= m_TimerWheel.size();
	}


	return nullptr;
}

3.创建ZinxTimer

  • 需要重写的函数最主要的是init和readfd

  • init函数中使用timerfd_create函数创建一个fd用于产生超时IO

  • Readfd函数中使用标准的read函数,消费每个超时IO。

  • 本类只负责产生1s的超时事件,这样可以让定时器更灵活

  • 产生1s的超时事件后,应该将该事件交给ZinxTimerDeliver处理

class ZinxTimer :public Ichannel
{
private:
	int m_fd = -1;
public:
	ZinxTimer();
	virtual ~ZinxTimer();

	// 通过 Ichannel 继承
	virtual bool Init() override;
	virtual bool ReadFd(std::string & _input) override;
	virtual bool WriteFd(std::string & _output) override;
	virtual void Fini() override;
	virtual int GetFd() override;
	virtual std::string GetChannelInfo() override;
	virtual AZinxHandler * GetInputNextStage(BytesMsg & _oInput) override;
};

//在init函数中创建fd
bool ZinxTimer::Init()
{
	bool bRet = false;
	int timerfd = -1;

    //选用CLOCK_MONOTONIC类型的时钟,不会受系统时间修改影响
	timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
	if (0 <= timerfd)
	{
        //设置第一次超时时间和后续超时时间都是1秒
		struct itimerspec period = { {1,0}, {1,0} };
		if (0 == timerfd_settime(timerfd, 0, &period, NULL))
		{
			m_fd = timerfd;
			bRet = true;
		}
		else
		{
			close(timerfd);
		}
	}

	return bRet;
}

bool ZinxTimer::ReadFd(std::string & _input)
{
	bool bRet = false;
	uint64_t over_times = 0;
    //调用read读取超时次数(大部分情况是1),将该64位数直接拷贝到输出参数字符串中(后续使用实再拷贝出来)
	if (sizeof(over_times) == read(m_fd, &over_times, sizeof(over_times)))
	{
		_input.append((char *)&over_times, sizeof(over_times));
		bRet = true;
	}
	return bRet;
}
//返回ZinxTimerDeliver类的单例对象,表示超时事件由ZinxTimerDeliver处理
AZinxHandler * ZinxTimer::GetInputNextStage(BytesMsg & _oInput)
{
	return &ZinxTimerDeliver::GetInstance();
}

4.测试

  • 创建SpeakHello类继承TimerOutProc,用来输出“hello world”

  • 将SpeakHello对象注册到ZinxTimerDeliver中

  • 创建ZinxTimer对象并添加到kernel

class SpeakHello :public TimerOutProc {
	// 通过 TimerOutProc 继承
	virtual void Proc() override
	{
		string hello = "hello world";
		ZinxKernel::Zinx_SendOut(hello, *poOut);
	}
	virtual int GetTimerSec() override
	{
		return 3;
	}
};
int main()
{
    SpeakHello speak;
    ZinxTimerDeliver::GetInstance().RegisterProcObject(speak);
	ZinxKernel::Zinx_Add_Channel(*(new ZinxTimer()));
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙Harmony应用开发—ArkTS声明式开发(绘制组件:Polygon)

多边形绘制组件。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 Polygon(value?: {width?: string | number, height?: string | number}) 从API version 9开始&#xff0…

python知识点总结(四)

这里写目录标题 1、Django 中的缓存是怎么用的&#xff1f;2、现有2元、3元、5元共三种面额的货币&#xff0c;如果需要找零99元&#xff0c;一共有多少种找零的方式?3、代码执行结果4、下面的代码执行结果为&#xff1a;5、说一下Python中变量的作用域。6、闭包7、python2与p…

STM32:定时器

TIM&#xff08;Timer&#xff09;定时器 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时72/65536/65536 不仅具备基本的定时中断功能&…

Grid布局

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Flex布局和Grid布局</title><style&…

每周编辑精选|微软开源 Orca-Math 高质量数学数据集、清华大学研究团队发布条件去噪扩散模型 SPDiff...

Orca-Math 是微软研究院发布的数学推理模型&#xff0c;该模型展示了较小的专业模型在特定领域的价值&#xff0c;它们可以匹配甚至超越更大模型的性能。微软近期开源了用于训练 Orca-Math 的 Orca-Math-200K 数学单词问题数据集&#xff0c;现已在 hyper.ai 官网提供下载&…

深入学习NIO三大核心:缓冲区 Buffer、通道Channel、Selector选择器

缓冲区 Buffer 一、简单介绍 Buffer&#xff0c;顾名思义就是缓冲区的意思&#xff0c;它是NIO中数据交换的载体&#xff0c;实质上是一种承载数据的容器。在上一篇BIO文章中我们提到BIO的工作模式是使用流来进行数据交换&#xff0c;并且根据操作的不同&#xff0c;分为输入…

Easy-Jmeter: 性能测试平台

目录 写在开始1 系统架构2 表结构设计3 测试平台生命周期4 分布式压测5 压力机管理6 用例管理6.1 新增、编辑用例6.2 调试用例6.3 启动测试6.4 动态控量6.5 测试详情6.6 环节日志6.7 实时数据6.8 测试结果 7 测试记录7 用例分析8 系统部署8.1普通部署8.2容器化部署 写在最后 写…

《LeetCode热题100》笔记题解思路技巧优化_Part_5

《LeetCode热题100》笔记&题解&思路&技巧&优化_Part_5 &#x1f60d;&#x1f60d;&#x1f60d; 相知&#x1f64c;&#x1f64c;&#x1f64c; 相识&#x1f622;&#x1f622;&#x1f622; 开始刷题图论&#x1f7e1;1. 岛屿数量&#x1f7e1;2. 腐烂的橘子…

量子计算+HPC!ORNL与Riverlane、Rigetti合作研发

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 编辑丨慕一 编译/排版丨沛贤 1000字丨8分钟阅读 近日&#xff0c;英国量子计算初创公司Riverlane和美国量子计算公司Rigetti Computing宣布将参与由美国能源部橡树岭国家实验室&#xff08;OR…

day03vue学习

day03 一、今日目标 1.生命周期 生命周期介绍生命周期的四个阶段生命周期钩子声明周期案例 2.综合案例-小黑记账清单 列表渲染添加/删除饼图渲染 3.工程化开发入门 工程化开发和脚手架项目运行流程组件化组件注册 4.综合案例-小兔仙首页 拆分模块-局部注册结构样式完善…

sheng的学习笔记-AI-残差网络-Residual Networks (ResNets)

目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 非常非常深的神经网络&#xff0c;网络发生了退化&#xff08;degradation&#xff09;的现象&#xff1a;随着网络层数的增多&#xff0c;训练集loss逐渐下降&#xff0c;然后趋于饱和&#xff0c;当你再增加网络深度的话&am…

【C++初阶】第七站:string类的初识(万字详解、细节拉满)

前言&#xff1a; &#x1f4cd;本文知识点&#xff1a;string的初识 本专栏&#xff1a;C 目录 一、什么是STL 二、STL的六大组件 三、STL的缺陷 四、为什么学习string类&#xff1f; 五、标准库中的string类 1、string类(了解) 2、string类的常用接口说明&#xff08;…

最新Java面试题2【2024初级】

下载链接&#xff1a;博主已将以上这些面试题整理成了一个面试手册&#xff0c;是PDF版的 互联网大厂面试题 1&#xff1a;阿里巴巴Java面试题 2&#xff1a;阿里云Java面试题-实习生岗 3&#xff1a;腾讯Java面试题-高级 4&#xff1a;字节跳动Java面试题 5&#xff1a;字…

如何通过ETL做数据转换

在数字化时代&#xff0c;数据被誉为新时代的石油&#xff0c;而数据的价值往往隐藏在海量数据背后的信息中。然而&#xff0c;海量的原始数据并不总是直接可用的&#xff0c;这就需要一种有效的工具来对数据进行提取、转换和加载&#xff08;ETL&#xff09;&#xff0c;从而将…

【排序】快速排序

原理 对于一个数组x&#xff0c;快速排序流程如下&#xff1a; 确定分界点a&#xff0c;可以取x[l]、x[r]、x[l r / 2]、随机&#xff08;四种都可以&#xff09;调整区间&#xff0c;使得&#xff1a;区间被分成 < a 和 > a的两部分&#xff0c;左边 < a&#xff…

转录因子/组蛋白修饰靶基因数据库:Cistrome DB使用教程

最近有小伙伴经常询问怎么预测转录因子的下游靶基因&#xff0c;以及预测一些组蛋白修饰影响的靶基因信息。今天就给大家介绍一下Cistrome数据浏览器&#xff08;Cistrome Data Browser&#xff09;。 Cistrome DB是来自人类和小鼠的ChIP-seq、ATAC-seq和DNase-seq数据的资源&…

huawei 华为交换机 配置手工模式链路聚合示例

组网需求 如 图 3-21 所示&#xff0c; SwitchA 和 SwitchB 通过以太链路分别都连接 VLAN10 和 VLAN20 的网络&#xff0c;SwitchA 和 SwitchB 之间有较大的数据流量。 用户希望SwitchA 和 SwitchB 之间能够提供较大的链路带宽来使相同 VLAN 间互相通信。 同时用户也希望能够提…

京津冀自动驾驶产业盛会“2024北京国际自动驾驶技术展览会”

随着科技的飞速发展&#xff0c;自动驾驶技术成为了汽车产业变革的热点和前沿。智能化、网联化已经成为推动汽车产业创新发展的重要力量&#xff0c;而自动驾驶技术则是其中的关键一环。它不仅能够提高道路安全性、缓解交通拥堵&#xff0c;还能为乘客带来更加舒适、便捷的出行…

【数据结构和算法初阶(C语言)】二叉树的顺序结构--堆的实现/堆排序/topk问题详解---二叉树学习日记②

目录 ​编辑 1.二叉树的顺序结构及实现 1.1 二叉树的顺序结构 2 堆的概念及结构 3 堆的实现 3.1堆的代码定义 3.2堆插入数据 3.3打印堆数据 3.4堆的数据的删除 3.5获取根部数据 3.6判断堆是否为空 3.7 堆的销毁 4.建堆以及堆排序 4.1 升序建大堆&#xff0c;降序建小堆 4.2堆…

AR/MR产品设计(二):如何用一双手完成与虚拟对象的自然交互

AR/MR产品设计&#xff08;二&#xff09;&#xff1a;如何用一双手完成与虚拟对象的自然交互 - 知乎 手是我们与现实世界交互最重要的方式&#xff0c;同样在虚实混合的世界中是最重要的交互方式 在AR/MR/VR的交互中&#xff0c;手势交互会作为XR的重要交互动作&#xff0c;因…
最新文章