1 Zinx框架總覽
2 三層模式的分析
3 三層重構(gòu)原有的功能 - 頭文件
三層結(jié)構(gòu)重構(gòu)原有功能
- 自定義消息類,繼承UserData,添加一個(gè)成員變量szUserData
- 定義多個(gè)Role類繼承Irole,重寫ProcMsg函數(shù),進(jìn)行不同處理
- 定義protocol類,繼承Iprotocol,重寫四個(gè)函數(shù),兩個(gè)函數(shù)時(shí)原始
數(shù)據(jù)和用戶數(shù)據(jù)之間的轉(zhuǎn)換;另兩個(gè)用來找消息處理對(duì)象和消息發(fā)
送對(duì)象。 - 定義channel類,繼承Ichannel,在getnextinputstage函數(shù)中返回協(xié)
議對(duì)象
3.1 通道層Stdin和Stdout類
通道類,派生自基礎(chǔ)處理者類,提供基于系統(tǒng)調(diào)用的數(shù)據(jù)收發(fā)功能
一般地,用戶應(yīng)該根據(jù)處理的文件(信息源)不同而創(chuàng)建通道類的子類或選用合適的實(shí)用類(已經(jīng)提供的通道類子類)來完成系統(tǒng)級(jí)文件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)
{
/*返回協(xié)議對(duì)象*/
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 協(xié)議層CmdCheck和CmdMsg類
3.2.1 CmdCheck單例模式
- 原始數(shù)據(jù)和業(yè)務(wù)數(shù)據(jù)相互函數(shù),開發(fā)者重寫該函數(shù),實(shí)現(xiàn)協(xié)議
- 獲取處理角色對(duì)象函數(shù),開發(fā)者應(yīng)該重寫該函數(shù),用來指定當(dāng)前產(chǎn)生的用戶數(shù)據(jù)消
- 獲取發(fā)送通道函數(shù),開發(fā)者應(yīng)該重寫該函數(shù),用來指定當(dāng)前字節(jié)流應(yīng)該由哪個(gè)通道對(duì)象發(fā)出
class CmdCheck :
public Iprotocol
{
CmdCheck();
virtual ~CmdCheck();
static CmdCheck *poSingle;
public:
// 通過 Iprotocol 繼承
/*原始數(shù)據(jù)和業(yè)務(wù)數(shù)據(jù)相互函數(shù),開發(fā)者重寫該函數(shù),實(shí)現(xiàn)協(xié)議*/
virtual UserData * raw2request(std::string _szInput) override;
virtual std::string * response2raw(UserData & _oUserData) override;
/*獲取處理角色對(duì)象函數(shù),開發(fā)者應(yīng)該重寫該函數(shù),用來指定當(dāng)前產(chǎn)生的用戶數(shù)據(jù)消息應(yīng)該傳遞給哪個(gè)角色處理*/
virtual Irole * GetMsgProcessor(UserDataMsg & _oUserDataMsg) override;
/*獲取發(fā)送通道函數(shù),開發(fā)者應(yīng)該重寫該函數(shù),用來指定當(dāng)前字節(jié)流應(yīng)該由哪個(gè)通道對(duì)象發(fā)出*/
virtual Ichannel * GetMsgSender(BytesMsg & _oBytes) override;
static CmdCheck *GetInstance() {
return poSingle;
}
std::string szOutChannel;
};
3.2.1.1 單例模式
構(gòu)造全局唯一的協(xié)議對(duì)象
#include "CmdCheck.h"
#include "CmdMsg.h"
#include "EchoRole.h"
using namespace std;
CmdCheck *CmdCheck::poSingle = new CmdCheck();
3.2.1.2 * 命令識(shí)別類向業(yè)務(wù)層不同類別做分發(fā)
通過是不是命令來進(jìn)行區(qū)分:if (isCmd)
Irole * CmdCheck::GetMsgProcessor(UserDataMsg & _oUserDataMsg)
{
szOutChannel = _oUserDataMsg.szInfo;
if ("stdin" == szOutChannel)
{
szOutChannel = "stdout";
}
/*根據(jù)命令不同,交給不同的處理role對(duì)象*/
auto rolelist = ZinxKernel::Zinx_GetAllRole();
auto pCmdMsg = dynamic_cast<CmdMsg *>(_oUserDataMsg.poUserData);
/*讀取當(dāng)前消息是否是命令*/
bool isCmd = pCmdMsg->isCmd;
Irole *pRetRole = NULL;
for (Irole *prole : rolelist)
{
if (isCmd)
{
auto pOutCtrl = dynamic_cast<OutputCtrl *>(prole);
if (NULL != pOutCtrl)
{
pRetRole = pOutCtrl;
break;
}
}
else
{
auto pDate = dynamic_cast<DatePreRole *>(prole);
if (NULL != pDate)
{
pRetRole = pDate;
break;
}
}
}
return pRetRole;
}
3.2.2 CmdMsg自定義用戶信息類,繼承UserData
class CmdMsg :
public UserData
{
public:
/*成員變量表示要回顯的字符串*/
std::string szUserData;
/*開啟輸出標(biāo)志*/
bool isOpen = true;
/*該消息是命令*/
bool isCmd = false;
/*要加前綴*/
bool needDatePre = false;
CmdMsg();
virtual ~CmdMsg();
};
3.3 業(yè)務(wù)層:回顯類, 輸出通道控制類, 日期前綴管理類
3.3.1 回顯對(duì)象EchoRole
主要有init, procmsg,fini三個(gè)函數(shù)
#pragma once
#include <zinx.h>
class EchoRole :
public Irole
{
public:
EchoRole();
virtual ~EchoRole();
// 通過 Irole 繼承
virtual bool Init() override;
virtual UserData * ProcMsg(UserData & _poUserData) override;
virtual void Fini() override;
};
- 容易出錯(cuò)的點(diǎn):參數(shù)一必須是一個(gè)堆對(duì)象
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 控制輸入輸出
- 寫一個(gè)關(guān)閉輸出的角色類,摘除輸出通道或添加輸出通道
- 在CmdMsg用戶數(shù)據(jù)類中添加開關(guān)標(biāo)志,是否是命令標(biāo)志
- 在協(xié)議類中,根據(jù)輸入字符串,設(shè)置開關(guān)標(biāo)志和是否是命令的標(biāo)志
- 在協(xié)議類分發(fā)消息時(shí),判斷是否是命令,是命令則發(fā)給關(guān)閉輸出角 色類,否則發(fā)給回顯角色類
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數(shù)據(jù)適配
4.1 工廠類 - 框架頭文件分析
- 產(chǎn)生tcp數(shù)據(jù)套接字通道類的抽象工廠類。
- 開發(fā)者需要重寫CreateTcpDataChannel函數(shù),來返回一個(gè)tcp通道對(duì)象。
- 般地,開發(fā)者應(yīng)該同時(shí)創(chuàng)建一對(duì)tcp通道類和工廠類
class IZinxTcpConnFact {
public:
virtual ZinxTcpData *CreateTcpDataChannel(int _fd) = 0;
};
- tcp監(jiān)聽通道類,這是一個(gè)實(shí)體類(不建議繼承該類)。
- 開發(fā)者可以直接創(chuàng)建tcp監(jiān)聽通道對(duì)象,
- 一般地,開發(fā)者應(yīng)該在該類的構(gòu)造函數(shù)中,指定一個(gè)tcp套接字通道類的工廠類,當(dāng)有連接到來后,該工廠類的成員方法會(huì)被調(diào)用
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通道實(shí)現(xiàn)
4.2.1 Tcp套接字通道通信類
- tcp數(shù)據(jù)套接字通道類,繼承通道類,該類也是一個(gè)抽象類,需要開發(fā)者繼承該類,重寫GetInputNextStage函數(shù)以指定讀取到的字節(jié)流的處理方式
// h
class myTcpData :public ZinxTcpData {
public:
myTcpData(int _fd) :ZinxTcpData(_fd) {}
// 通過 ZinxTcpData 繼承
virtual AZinxHandler* GetInputNextStage(BytesMsg& _oInput) override;
};
- Q: Ichannel對(duì)象讀取到的數(shù)據(jù)給誰了?
- 給該對(duì)象調(diào)用GetInputNextStage函數(shù)返回的對(duì)象
AZinxHandler* myTcpData::GetInputNextStage(BytesMsg& _oInput)
{
/*返回協(xié)議對(duì)象*/
return CmdCheck::GetInstance();
}
- Q: Iprotocol對(duì)象轉(zhuǎn)換出的用戶請(qǐng)求給誰了?
- 給該對(duì)象調(diào)用GetMsgProcessor函數(shù)返回的對(duì)象
4.2.2 tcp數(shù)據(jù)套接字通道類的工廠類
- 產(chǎn)生tcp數(shù)據(jù)套接字通道類的抽象工廠類,開發(fā)者需要重寫CreateTcpDataChannel函數(shù),來返回一個(gè)tcp通道對(duì)象
一般地,開發(fā)者應(yīng)該同時(shí)創(chuàng)建一對(duì)tcp通道類和工廠類
// h
class myFact :public IZinxTcpConnFact {
// 通過 IZinxTcpConnFact 繼承
virtual ZinxTcpData* CreateTcpDataChannel(int _fd) override;
};
ZinxTcpData* myFact::CreateTcpDataChannel(int _fd)
{
return new myTcpData(_fd);
}
5 時(shí)間輪定時(shí)器
5.1 timerfd產(chǎn)生超時(shí)事件
timerfd_create()返回定時(shí)器文件描述符
timerfd_settime()設(shè)置定時(shí)周期,立刻開始計(jì)時(shí)
read,讀取當(dāng)當(dāng)前定時(shí)器超時(shí)的次數(shù),沒超時(shí)會(huì)阻塞.
一般地,會(huì)將定時(shí)器文件描述符結(jié)合IO多路復(fù)用使用
5.1.1 測(cè)試代碼
#include<sys/timerfd.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.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");
}
}
文章來源:http://www.zghlxwxcb.cn/news/detail-833955.html
5.2 時(shí)間輪設(shè)置
單例模式文章來源地址http://www.zghlxwxcb.cn/news/detail-833955.html
AZinxHandler * ZinxTimerChannel::GetInputNextStage(BytesMsg & _oInput)
{
return &TimerOutMng::GetInstance();
}
TimerOutMng TimerOutMng::single;
5.2.1 時(shí)間輪的定義
// h
class TimerOutProc {
public:
virtual void Proc() = 0;
virtual int GetTimeSec() = 0;
/*所剩圈數(shù)*/
int iCount = -1;
};
- vector存儲(chǔ)輪的齒
- 每個(gè)齒里用list存每個(gè)定時(shí)任務(wù)
- 每個(gè)定時(shí)任務(wù)需要記錄剩余圈數(shù)
- 時(shí)間輪類中要有一個(gè)刻度,每秒進(jìn)一步
TimerOutMng::TimerOutMng()
{
/*創(chuàng)建10個(gè)齒*/
for (int i = 0; i < 10; i++)
{
list<TimerOutProc *> tmp;
m_timer_wheel.push_back(tmp);
}
}
5.2.2 時(shí)間輪的移動(dòng)
// h
class TimerOutMng :public AZinxHandler {
std::vector<std::list<TimerOutProc *> > m_timer_wheel;
int cur_index = 0;
static TimerOutMng single;
TimerOutMng();
public:
/*處理超時(shí)事件,遍歷所有超時(shí)任務(wù)*/
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;
}
};
- 移動(dòng)當(dāng)前刻度
- 遍歷當(dāng)前齒中的任務(wù)列表
- 若圈數(shù)為0,則執(zhí)行處理函數(shù),摘除本節(jié)點(diǎn),重新添加
- 否則,圈數(shù)–
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)
{
/*移動(dòng)刻度*/
cur_index++;
cur_index %= 10;
list<TimerOutProc *> m_cache;
/*遍歷當(dāng)前刻度所有節(jié)點(diǎn),指向處理函數(shù)或圈數(shù)-1,*/
for (auto itr = m_timer_wheel[cur_index].begin(); itr != m_timer_wheel[cur_index].end(); )
{
if ((*itr)->iCount <= 0)
{
/*緩存待處理的超時(shí)節(jié)點(diǎn)*/
m_cache.push_back(*itr);
auto ptmp = *itr;
itr = m_timer_wheel[cur_index].erase(itr);
AddTask(ptmp);
}
else
{
(*itr)->iCount--;
++itr;
}
}
/*統(tǒng)一待處理超時(shí)任務(wù)*/
for (auto task : m_cache)
{
task->Proc();
}
}
return nullptr;
}
5.2.3 添加和刪除任務(wù)
5.2.3.1 添加任務(wù)
- 計(jì)算當(dāng)前任務(wù)在哪個(gè)齒上
- 添加該任務(wù)到該齒對(duì)應(yīng)的list里
- 計(jì)算所需圈數(shù)記錄到任務(wù)中
void TimerOutMng::AddTask(TimerOutProc * _ptask)
{
/*計(jì)算當(dāng)前任務(wù)需要放到哪個(gè)齒上*/
int index = (_ptask->GetTimeSec() + cur_index) % 10;
/*把任務(wù)存到該齒上*/
m_timer_wheel[index].push_back(_ptask);
/*計(jì)算所需圈數(shù)*/
_ptask->iCount = _ptask->GetTimeSec() / 10;
}
5.2.3.2 刪除任務(wù)
- 遍歷所有齒
- 在每個(gè)齒中遍歷所有節(jié)點(diǎn)
- 若找到則刪除并返回
void TimerOutMng::DelTask(TimerOutProc * _ptask)
{
/*遍歷時(shí)間輪所有齒,刪掉任務(wù)*/
for (list<TimerOutProc *> &chi : m_timer_wheel)
{
for (auto task : chi)
{
if (task == _ptask)
{
chi.remove(_ptask);
return;
}
}
}
}
5.3 定時(shí)器設(shè)置
5.3.1 定時(shí)器定義
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 定時(shí)器初始化
/*創(chuàng)建定時(shí)器文件描述符*/
bool ZinxTimerChannel::Init()
{
bool bRet = false; //判斷成功或者失敗
/*創(chuàng)建文件描述符*/
int iFd = timerfd_create(CLOCK_MONOTONIC, 0);
if (0 <= iFd)
{
/*設(shè)置定時(shí)周期*/
struct itimerspec period = {
{1,0}, {1,0}
};
if (0 == timerfd_settime(iFd, 0, &period, NULL))
{
bRet = true;
m_TimerFd = iFd;
}
}
return bRet;
}
/*讀取超時(shí)次數(shù)*/
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;
}
/*關(guān)閉文件描述符*/
void ZinxTimerChannel::Fini()
{
close(m_TimerFd);
m_TimerFd = -1;
}
/*返回當(dāng)前的定時(shí)器文件描述符*/
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();
到了這里,關(guān)于C/C++輕量級(jí)并發(fā)TCP服務(wù)器框架Zinx-游戲服務(wù)器開發(fā)002:框架學(xué)習(xí)-按照三層結(jié)構(gòu)模式重構(gòu)測(cè)試代碼+Tcp數(shù)據(jù)適配+時(shí)間輪定時(shí)器的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!