网站开发设计流程文档,单页网站制作工具,WordPress文章数据转emlog,赫章网站建设文章目录 1 Zinx框架总览2 三层模式的分析3 三层重构原有的功能 - 头文件3.1 通道层Stdin和Stdout类3.1.2 StdInChannel3.1.2 StdOutChannel 3.2 协议层CmdCheck和CmdMsg类3.2.1 CmdCheck单例模式3.2.1.1 单例模式3.2.1.2 * 命令识别类向业务层不同类别做分发 3.2.2 CmdMsg自定… 文章目录 1 Zinx框架总览2 三层模式的分析3 三层重构原有的功能 - 头文件3.1 通道层Stdin和Stdout类3.1.2 StdInChannel3.1.2 StdOutChannel 3.2 协议层CmdCheck和CmdMsg类3.2.1 CmdCheck单例模式3.2.1.1 单例模式3.2.1.2 * 命令识别类向业务层不同类别做分发 3.2.2 CmdMsg自定义用户信息类继承UserData 3.3 业务层回显类, 输出通道控制类, 日期前缀管理类3.3.1 回显对象EchoRole3.3.2 控制输入输出3.3.3 日期管理类 4 Tcp数据适配4.1 工厂类 - 框架头文件分析4.2 tcp通道实现4.2.1 Tcp套接字通道通信类4.2.2 tcp数据套接字通道类的工厂类 5 时间轮定时器5.1 timerfd产生超时事件5.1.1 测试代码 5.2 时间轮设置5.2.1 时间轮的定义5.2.2 时间轮的移动5.2.3 添加和删除任务5.2.3.1 添加任务5.2.3.2 删除任务 5.3 定时器设置5.3.1 定时器定义5.3.2 定时器初始化5.3.3 输出hello world 1 Zinx框架总览 2 三层模式的分析 3 三层重构原有的功能 - 头文件
三层结构重构原有功能
自定义消息类继承UserData添加一个成员变量szUserData定义多个Role类继承Irole重写ProcMsg函数进行不同处理定义protocol类继承Iprotocol重写四个函数两个函数时原始 数据和用户数据之间的转换另两个用来找消息处理对象和消息发 送对象。定义channel类继承Ichannel在getnextinputstage函数中返回协 议对象
3.1 通道层Stdin和Stdout类
通道类派生自基础处理者类提供基于系统调用的数据收发功能 一般地用户应该根据处理的文件信息源不同而创建通道类的子类或选用合适的实用类已经提供的通道类子类来完成系统级文件IO
class StdInChannel :public Ichannel
{
public:StdInChannel();virtual ~StdInChannel();// 通过 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;
};class StdOutChannel :public Ichannel
{// 通过 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;
};3.1.2 StdInChannel
bool StdInChannel::ReadFd(std::string _input)
{cin _input;return true;
}bool StdInChannel::WriteFd(std::string _output)
{return false;
}int StdInChannel::GetFd()
{return 0;
}std::string StdInChannel::GetChannelInfo()
{return stdin;
}AZinxHandler* StdInChannel::GetInputNextStage(BytesMsg _oInput)
{/*返回协议对象*/return CmdCheck::GetInstance();
}3.1.2 StdOutChannel
bool StdOutChannel::ReadFd(std::string _input)
{return false;
}bool StdOutChannel::WriteFd(std::string _output)
{cout _output endl;return true;
}int StdOutChannel::GetFd()
{return 1;
}std::string StdOutChannel::GetChannelInfo()
{return stdout;
}AZinxHandler* StdOutChannel::GetInputNextStage(BytesMsg _oInput)
{return nullptr;
}
3.2 协议层CmdCheck和CmdMsg类
3.2.1 CmdCheck单例模式
原始数据和业务数据相互函数开发者重写该函数实现协议获取处理角色对象函数开发者应该重写该函数用来指定当前产生的用户数据消获取发送通道函数开发者应该重写该函数用来指定当前字节流应该由哪个通道对象发出
class CmdCheck :public Iprotocol
{CmdCheck();virtual ~CmdCheck();static CmdCheck *poSingle;
public:// 通过 Iprotocol 继承/*原始数据和业务数据相互函数开发者重写该函数实现协议*/virtual UserData * raw2request(std::string _szInput) override;virtual std::string * response2raw(UserData _oUserData) override;/*获取处理角色对象函数开发者应该重写该函数用来指定当前产生的用户数据消息应该传递给哪个角色处理*/virtual Irole * GetMsgProcessor(UserDataMsg _oUserDataMsg) override;/*获取发送通道函数开发者应该重写该函数用来指定当前字节流应该由哪个通道对象发出*/virtual Ichannel * GetMsgSender(BytesMsg _oBytes) override;static CmdCheck *GetInstance() {return poSingle;}std::string szOutChannel;
};3.2.1.1 单例模式
构造全局唯一的协议对象
#include CmdCheck.h
#include CmdMsg.h
#include EchoRole.h
using namespace std;CmdCheck *CmdCheck::poSingle new CmdCheck();
3.2.1.2 * 命令识别类向业务层不同类别做分发
通过是不是命令来进行区分if (isCmd)
Irole * CmdCheck::GetMsgProcessor(UserDataMsg _oUserDataMsg)
{szOutChannel _oUserDataMsg.szInfo;if (stdin szOutChannel){szOutChannel stdout;}/*根据命令不同交给不同的处理role对象*/auto rolelist ZinxKernel::Zinx_GetAllRole();auto pCmdMsg dynamic_castCmdMsg *(_oUserDataMsg.poUserData);/*读取当前消息是否是命令*/bool isCmd pCmdMsg-isCmd;Irole *pRetRole NULL;for (Irole *prole : rolelist){if (isCmd){auto pOutCtrl dynamic_castOutputCtrl *(prole);if (NULL ! pOutCtrl){pRetRole pOutCtrl;break;}}else{auto pDate dynamic_castDatePreRole *(prole);if (NULL ! pDate){pRetRole pDate;break;}}}return pRetRole;
}
3.2.2 CmdMsg自定义用户信息类继承UserData
class CmdMsg :public UserData
{
public:/*成员变量表示要回显的字符串*/std::string szUserData;/*开启输出标志*/bool isOpen true;/*该消息是命令*/bool isCmd false;/*要加前缀*/bool needDatePre false;CmdMsg();virtual ~CmdMsg();
};
3.3 业务层回显类, 输出通道控制类, 日期前缀管理类
3.3.1 回显对象EchoRole
主要有init procmsgfini三个函数
#pragma once
#include zinx.hclass EchoRole :public Irole
{
public:EchoRole();virtual ~EchoRole();// 通过 Irole 继承virtual bool Init() override;virtual UserData * ProcMsg(UserData _poUserData) override;virtual void Fini() override;
};容易出错的点参数一必须是一个堆对象
UserData * EchoRole::ProcMsg(UserData _poUserData)
{/*写出去*/GET_REF2DATA(CmdMsg, input, _poUserData);CmdMsg *pout new CmdMsg(input);ZinxKernel::Zinx_SendOut(*pout, *(CmdCheck::GetInstance()));return nullptr;
}
3.3.2 控制输入输出 写一个关闭输出的角色类摘除输出通道或添加输出通道在CmdMsg用户数据类中添加开关标志是否是命令标志在协议类中根据输入字符串设置开关标志和是否是命令的标志在协议类分发消息时判断是否是命令是命令则发给关闭输出角 色类否则发给回显角色类 class OutputCtrl :public Irole {// 通过 Irole 继承virtual bool Init() override;virtual UserData * ProcMsg(UserData _poUserData) override;virtual void Fini() override;Ichannel *pOut NULL;
};3.3.3 日期管理类
class DatePreRole :public Irole {// 通过 Irole 继承virtual bool Init() override;virtual UserData * ProcMsg(UserData _poUserData) override;virtual void Fini() override;bool needAdd false;
};4 Tcp数据适配
4.1 工厂类 - 框架头文件分析
产生tcp数据套接字通道类的抽象工厂类。 开发者需要重写CreateTcpDataChannel函数来返回一个tcp通道对象。般地开发者应该同时创建一对tcp通道类和工厂类
class IZinxTcpConnFact {
public:virtual ZinxTcpData *CreateTcpDataChannel(int _fd) 0;
};tcp监听通道类这是一个实体类不建议继承该类。 开发者可以直接创建tcp监听通道对象一般地开发者应该在该类的构造函数中指定一个tcp套接字通道类的工厂类当有连接到来后该工厂类的成员方法会被调用
class ZinxTCPListen :public Ichannel
{
private:unsigned short m_usPort 0;int m_fd -1;IZinxTcpConnFact *m_ConnFac NULL;public:ZinxTCPListen(unsigned short _usPort, IZinxTcpConnFact *_pConnFac) :m_usPort(_usPort), m_ConnFac(_pConnFac){}virtual ~ZinxTCPListen();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);
};4.2 tcp通道实现 4.2.1 Tcp套接字通道通信类
tcp数据套接字通道类继承通道类该类也是一个抽象类需要开发者继承该类重写GetInputNextStage函数以指定读取到的字节流的处理方式
// h
class myTcpData :public ZinxTcpData {
public:myTcpData(int _fd) :ZinxTcpData(_fd) {}// 通过 ZinxTcpData 继承virtual AZinxHandler* GetInputNextStage(BytesMsg _oInput) override;
};Q: Ichannel对象读取到的数据给谁了 给该对象调用GetInputNextStage函数返回的对象
AZinxHandler* myTcpData::GetInputNextStage(BytesMsg _oInput)
{/*返回协议对象*/return CmdCheck::GetInstance();
}Q: Iprotocol对象转换出的用户请求给谁了 给该对象调用GetMsgProcessor函数返回的对象
4.2.2 tcp数据套接字通道类的工厂类
产生tcp数据套接字通道类的抽象工厂类开发者需要重写CreateTcpDataChannel函数来返回一个tcp通道对象 一般地开发者应该同时创建一对tcp通道类和工厂类
// h
class myFact :public IZinxTcpConnFact {// 通过 IZinxTcpConnFact 继承virtual ZinxTcpData* CreateTcpDataChannel(int _fd) override;
};ZinxTcpData* myFact::CreateTcpDataChannel(int _fd)
{return new myTcpData(_fd);
}5 时间轮定时器
5.1 timerfd产生超时事件 timerfd_create()返回定时器文件描述符 timerfd_settime()设置定时周期立刻开始计时 read读取当当前定时器超时的次数没超时会阻塞. 一般地会将定时器文件描述符结合IO多路复用使用 5.1.1 测试代码
#includesys/timerfd.h
#includestdio.h
#includeunistd.h
#includestdlib.h
int main()
{int iTimerfd timerfd_create(CLOCK_MONOTONIC, 0);struct itimerspec period{{5, 0},{5, 0}};timerfd_settime(iTimerfd,0, period,NULL);__uint64_t count 0;while(1) {read(iTimerfd, count, sizeof(count));puts(time out);}
}5.2 时间轮设置
单例模式
AZinxHandler * ZinxTimerChannel::GetInputNextStage(BytesMsg _oInput)
{return TimerOutMng::GetInstance();
}TimerOutMng TimerOutMng::single;5.2.1 时间轮的定义
// h
class TimerOutProc {
public:virtual void Proc() 0;virtual int GetTimeSec() 0;/*所剩圈数*/int iCount -1;
};
vector存储轮的齿每个齿里用list存每个定时任务每个定时任务需要记录剩余圈数时间轮类中要有一个刻度每秒进一步
TimerOutMng::TimerOutMng()
{/*创建10个齿*/for (int i 0; i 10; i){listTimerOutProc * tmp;m_timer_wheel.push_back(tmp);}
}5.2.2 时间轮的移动
// h
class TimerOutMng :public AZinxHandler {std::vectorstd::listTimerOutProc * m_timer_wheel;int cur_index 0;static TimerOutMng single;TimerOutMng();
public:/*处理超时事件,遍历所有超时任务*/virtual IZinxMsg * InternelHandle(IZinxMsg _oInput) override;virtual AZinxHandler * GetNextHandler(IZinxMsg _oNextMsg) override;void AddTask(TimerOutProc *_ptask);void DelTask(TimerOutProc *_ptask);static TimerOutMng GetInstance() {return single;}
};移动当前刻度遍历当前齿中的任务列表若圈数为0则执行处理函数摘除本节点重新添加否则圈数–
IZinxMsg * TimerOutMng::InternelHandle(IZinxMsg _oInput)
{unsigned long iTimeoutCount 0;GET_REF2DATA(BytesMsg, obytes, _oInput);obytes.szData.copy((char *)iTimeoutCount, sizeof(iTimeoutCount), 0);while (iTimeoutCount-- 0){/*移动刻度*/cur_index;cur_index % 10;listTimerOutProc * m_cache;/*遍历当前刻度所有节点指向处理函数或圈数-1*/for (auto itr m_timer_wheel[cur_index].begin(); itr ! m_timer_wheel[cur_index].end(); ){if ((*itr)-iCount 0){/*缓存待处理的超时节点*/m_cache.push_back(*itr);auto ptmp *itr;itr m_timer_wheel[cur_index].erase(itr);AddTask(ptmp);}else{(*itr)-iCount--;itr;}}/*统一待处理超时任务*/for (auto task : m_cache){task-Proc();}}return nullptr;
}
5.2.3 添加和删除任务
5.2.3.1 添加任务
计算当前任务在哪个齿上添加该任务到该齿对应的list里计算所需圈数记录到任务中
void TimerOutMng::AddTask(TimerOutProc * _ptask)
{/*计算当前任务需要放到哪个齿上*/int index (_ptask-GetTimeSec() cur_index) % 10;/*把任务存到该齿上*/m_timer_wheel[index].push_back(_ptask);/*计算所需圈数*/_ptask-iCount _ptask-GetTimeSec() / 10;
}5.2.3.2 删除任务
遍历所有齿在每个齿中遍历所有节点若找到则删除并返回
void TimerOutMng::DelTask(TimerOutProc * _ptask)
{/*遍历时间轮所有齿删掉任务*/for (listTimerOutProc * chi : m_timer_wheel){for (auto task : chi){if (task _ptask){chi.remove(_ptask);return;}}}
}5.3 定时器设置
5.3.1 定时器定义
class ZinxTimerChannel :public Ichannel
{int m_TimerFd -1;public:ZinxTimerChannel();virtual ~ZinxTimerChannel();// 通过 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;
};5.3.2 定时器初始化
/*创建定时器文件描述符*/
bool ZinxTimerChannel::Init()
{bool bRet false; //判断成功或者失败/*创建文件描述符*/int iFd timerfd_create(CLOCK_MONOTONIC, 0);if (0 iFd){/*设置定时周期*/struct itimerspec period {{1,0}, {1,0}};if (0 timerfd_settime(iFd, 0, period, NULL)){bRet true;m_TimerFd iFd; }}return bRet;
}
/*读取超时次数*/
bool ZinxTimerChannel::ReadFd(std::string _input)
{bool bRet false;char buff[8] { 0 };if (sizeof(buff) read(m_TimerFd, buff, sizeof(buff))){bRet true;_input.assign(buff, sizeof(buff));}return bRet;
}bool ZinxTimerChannel::WriteFd(std::string _output)
{return false;
}/*关闭文件描述符*/
void ZinxTimerChannel::Fini()
{close(m_TimerFd);m_TimerFd -1;
}/*返回当前的定时器文件描述符*/
int ZinxTimerChannel::GetFd()
{return m_TimerFd;
}std::string ZinxTimerChannel::GetChannelInfo()
{return TimerFd; // 名字随便起的
}5.3.3 输出hello world
class output_hello :public AZinxHandler {// 通过 AZinxHandler 继承virtual IZinxMsg * InternelHandle(IZinxMsg _oInput) override{auto pchannel ZinxKernel::Zinx_GetChannel_ByInfo(stdout);std::string output hello world;ZinxKernel::Zinx_SendOut(output, *pchannel);return nullptr;}virtual AZinxHandler * GetNextHandler(IZinxMsg _oNextMsg) override{return nullptr;}
} *pout_hello new output_hello();