Zinx框架的高级用法

news2024/10/6 12:20:24

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

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.coloradmin.cn/o/1526003.html

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

相关文章

你真的懂Linux线程和信号的关系吗?

前言&#xff1a; 讲到信号&#xff0c;我们更多的是站在进程的角度去理解信号&#xff0c;当一个进程有多个线程的时候&#xff0c;那么进程&#xff0c;线程&#xff0c;信号三者之间会是什么样的关系呢&#xff1f; 1.线程与信号的关系 线程与信号关系要遵循以下标准&…

【Node.js从基础到高级运用】十四、Node.js 错误处理与日志记录

引言 在这篇博客文章中&#xff0c;我们将深入探讨Node.js中的错误处理和日志记录的最佳实践。我们会了解如何在Node.js应用程序中有效地捕获和处理错误&#xff0c;并利用日志库如morgan来记录应用程序的活动和错误信息。 第1部分&#xff1a;Node.js中的错误处理 同步代码中…

Aigtek电压放大器的作用及优点是什么

电压放大器是电子技术领域中重要的设备&#xff0c;其作用是将输入信号的电压放大到所需的输出电压水平。电压放大器具有多种优点&#xff0c;下面安泰电子将详细介绍其作用及主要优点。 电压放大器的主要作用是增加信号的电压幅值。通过放大信号的电压&#xff0c;可以增强信号…

苹果MacOS电脑使用内网穿透轻松远程桌面本地Windows系统电脑

文章目录 1. 测试本地局域网内远程控制1.1 Windows打开远程桌面1.2 局域网远程控制windows 2. 测试Mac公网远程控制windows2.1 在windows电脑上安装cpolar2.2 Mac公网远程windows 3. 配置公网固定TCP地址 日常工作生活中&#xff0c;有时候会涉及到不同设备不同操作系统之间需要…

Soul打造多元社交元宇宙,满足年轻人多样化的设计需求

近年来,元宇宙概念备受瞩目,成为全球关注的热门话题。其中,Soul App凭借其独特的Gamified游戏化社交理念,早早成为了元宇宙的开拓者之一。通过持续布局元宇宙赛道,加大技术创新力度,以及创新平台玩法和场景,Soul为用户打造了一个真实、温暖、多元的社交元宇宙,深受Z世代用户的喜…

Python脚本:用py处理PDF的五大功能

一、代码 【第三方库】3个 【Py版本】3.9 【使用前提】关闭所有的word文档 import os from datetime import datetime from docx2pdf import convert from pdf2docx import parse from PyPDF2 import PdfMerger from PyPDF2 import PdfReader,PdfWriter#将文件夹中的所有Wo…

Python从COCO数据集中抽取某类别的数据

1、问题描述 今天需要训练一个人工智能检测模型&#xff0c;用于检测图片或视频中的人。自行收集训练数据费时费力&#xff0c;因而选择从公开数据集COCO中进行抽取。 2、数据准备 2.1 下载 COCO2017 数据集 train:http://images.cocodataset.org/zips/train2017.zip valid…

杉德支付配合调查 - 数字藏品服务

最近&#xff0c;数字收藏品平台淘派发布了一则公告&#xff0c;宣布支付通道杉德已暂停接口服务&#xff0c;以配合调查。 近期发现多个异常账户&#xff0c;涉嫌盗取他人信息和银行卡&#xff0c;利用平台从事非法交易。淘派已第一时间报警&#xff0c;协助警方追回资金(回执…

学生能力是如何被封印的

为了避免无法发表&#xff0c;需要借助人工智能。 为什么一直没写 这个主题其实很多年前就明晰了&#xff0c;但是没有勇气去写出来。 责任全在人工智能 如下是人工智能的回复&#xff0c;如有责任&#xff0c;全都是人工智能的责任。 在学生的情境中&#xff0c;“能力被封印…

Kigo Netflix Video Downloader:Mac与Windows用户的视频下载利器

随着网络的发展和普及&#xff0c;越来越多的人开始使用在线流媒体服务来观看电影、电视剧和其他视频内容。其中&#xff0c;Netflix是世界上最受欢迎的流媒体平台之一。然而&#xff0c;对于想要离线观看这些视频内容的用户来说&#xff0c;下载它们可能会变得有些困难。幸运的…

一文教会你SpringBoot是如何启动的

SpringBoot启动流程分析 流程图 源码剖析 运行Application.run()方法 我们在创建好一个 SpringBoot 程序之后&#xff0c;肯定会包含一个类&#xff1a;xxxApplication&#xff0c;我们也是通过这个类来启动我们的程序的&#xff08;梦开始的地方&#xff09;&#xff0c;而…

J.砍树【蓝桥杯】树上差分+LCA

树上差分 多次对树上的一些路径做加法操作&#xff0c;然后询问某个点或某条边经过操作后的值&#xff0c;就要考虑树上差分了。 点差分 模拟这个过程 对x到y路径上的点权值均1&#xff0c;可以等价成对x和y的权值加1&#xff0c;对lca的权值-1&#xff0c;对fa[lca]的权值-…

操作系统知识-存储管理+文件管理管理-嵌入式系统设计师备考笔记

0、前言 本专栏为个人备考软考嵌入式系统设计师的复习笔记&#xff0c;未经本人许可&#xff0c;请勿转载&#xff0c;如发现本笔记内容的错误还望各位不吝赐教&#xff08;笔记内容可能有误怕产生错误引导&#xff09;。 本章的主要内容见下图&#xff1a; 1、存储管理&#…

【数据结构取经之路】归并排序

简介 归并排序是建立在归并操作上的一种有效&#xff0c;稳定的排序算法&#xff0c;该算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子序列有序&#xff0c;…

在Docker上传我们自己的镜像(以springboot项目为例)

首先确定好在我们的centOS服务器上已经安装并配置好docker 配置自己的springboot镜像并运行 获取springboot的jar包 maven clean--》mavenue package --》复制target目录下生成的jar包 在服务器选择一个文件夹上传jar包&#xff0c;我这里选用的文件夹叫做/opt/dockertest…

如何在HomeAssistant智能家居系统中添加HACS集成并实现无公网IP远程连接家中设备

文章目录 基本条件一、下载HACS源码二、添加HACS集成三、绑定米家设备 ​ 上文介绍了如何实现群晖Docker部署HomeAssistant&#xff0c;通过内网穿透在户外控制家庭中枢。本文将介绍如何安装HACS插件商店&#xff0c;将米家&#xff0c;果家设备接入 Home Assistant。 基本条件…

Python the code is unreachable

Python the code is unreachable 正文 正文 相信有不少小伙伴在使用 Python 的时候有时候会遇到 the code is unreachable 这样的 warning 提示。这种提示表示在我们当前书写的代码种有一部分代码被屏蔽了。可能会存在潜在的 bug&#xff0c;需要我们注意&#xff0c;那么什么…

2023年蓝桥杯省赛——幸运数字

目录 题目链接&#xff1a;0幸运数字 - 蓝桥云课 (lanqiao.cn) 解法 思路 高级思路 总结 题目链接&#xff1a;0幸运数字 - 蓝桥云课 (lanqiao.cn) 解法 首先是我写了差不多一个小时的解法&#xff0c;裂开了&#xff0c;为什么我如此废物 思路 寻找第2023个在二进制、八…

弱电工程是什么?常见的类型有哪些?

一、什么是弱电工程?强电和弱电的区别有哪些? 弱电工程又叫智能建筑&#xff0c;也叫系统集成工程&#xff0c;所有与信息有关的都属于弱电这一块的。弱电是相对于强电而言的强电和弱电从概念上讲&#xff0c;一般是容易区别的&#xff0c;主要区别是用途的不同。强电是用作…

Compute Express Link (CXL): An Open Interconnect for Cloud Infrastructure——论文阅读

DAC 2023 Paper CXL论文阅读笔记整理 背景 Compute Express Link是一种开放的行业标准互连&#xff0c;在PCI Express&#xff08;PCIe&#xff09;之上提供缓存和内存语义&#xff0c;具有资源池和织物功能。本文探讨了CXL在解决云基础设施中的一些挑战方面的作用。 CXL主要…