北京做网站多少钱,莱西网站建设,wordpress一数据库多网站,多用户网站建设一、使用框架提供的实用类
zinx框架已经提供了常用的IO通道类-TCP。
阅读Tcp相关类的使用文档#xff0c;将之前的3个案例用TCP的方式实现。
步骤#xff1a; 创建Tcp数据通道类继承ZinxTcpData#xff0c;重写GetInputNextStage函数#xff0c;内容跟之前标准输入通道类…一、使用框架提供的实用类
zinx框架已经提供了常用的IO通道类-TCP。
阅读Tcp相关类的使用文档将之前的3个案例用TCP的方式实现。
步骤 创建Tcp数据通道类继承ZinxTcpData重写GetInputNextStage函数内容跟之前标准输入通道类的内容完全相同但不直接构造对象。 创建Tcp工厂类重写CreateTcpDataChannel函数只构造一个Tcp数据通道对象返回对象指针 创建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;//时间轮向量每个坑中放一个multimapmultmap元素是圈数和定时器节点std::vectorstd::multimapint, 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(pairint, 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);//获取当前超时的次数一般是1oBytes.szData.copy((char *)counts, sizeof(counts), 0);for (int i 0; i counts; i){//定义list存储超时的定时器节点方便重新插入时间轮和后续回调listWheelNode 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()));
}