zinx框架学习
TCP篇
GameChannel.h
#include <ZinxTCP.h>
#include "GameProtocol.h"
class GameChannel
:public ZinxTcpData
{
public:
GameChannel(int _fd):ZinxTcpData(_fd){}
~GameChannel() {}
virtual AZinxHandler * GetInputNextStage(BytesMsg & _oInput);
public:
GameProtocol *m_proto = NULL;
};
class GameConnFact
:public IZinxTcpConnFact{
virtual ZinxTcpData *CreateTcpDataChannel(int _fd);
};
GameChannel.cpp
#include "GameChannel.h"
//class GameChannel
AZinxHandler *GameChannel::GetInputNextStage(BytesMsg & _oInput){
return m_proto;
}
//class GameConnFact
ZinxTcpData *GameConnFact::CreateTcpDataChannel(int _fd){
// 创建tcp通道对象
auto pChannel = new GameChannel(_fd);
// 创建协议对象
auto pProtocol = new GameProtocol();
// 绑定协议对象
pChannel->m_proto = pProtocol;
pProtocol->m_channel = pChannel;
// 将协议对象添加到kernel
ZinxKernel::Zinx_Add_Proto(*pProtocol);
return pChannel;
}
GameMsg.h
#include <zinx.h>
#include <google/protobuf/message.h>
#include <list>
class GameMsg
:public UserData
{
public:
// 用户的请求信息
google::protobuf::Message * pMsg = NULL;
enum MSG_TYPE{
MSG_TYPE_LOGIN_ID_NAME = 1,
MSG_TYPE_CHAT_CONTENT = 2,
MSG_TYPE_NEW_POSTION = 3,
MSG_TYPE_BROADCAST = 200,
MSG_TYPE_LOGOFF_ID_NAME = 201,
MSG_TYPE_SRD_POSTION = 202
}enMsgType;
public:
~GameMsg();
// 已知消息内容创建消息对象
GameMsg(MSG_TYPE _type,google::protobuf::Message *_pMsg);
// 将字节流内容转换成消息结构
GameMsg(MSG_TYPE _type,std::string _stream);
// 序列化本地消息
std::string serialize();
};
class MultiMsg
:public UserData
{
public:
std::list<GameMsg*> m_Msgs;
};
GameMsg.cpp
#include "GameMsg.h"
#include "msg.pb.h"
//class GameMsg
GameMsg::~GameMsg(){
}
// 已知消息内容创建消息对象
GameMsg::GameMsg(MSG_TYPE _type,google::protobuf::Message *_pMsg):enMsgType(_type),pMsg(_pMsg){}
// 将字节流内容转换成消息结构
GameMsg::GameMsg(MSG_TYPE _type,std::string _stream){
// 通过简单工厂构造具体的消息对象
switch (_type) {
case GameMsg::MSG_TYPE_LOGIN_ID_NAME:
pMsg = new pb::SyncPid();
break;
case GameMsg::MSG_TYPE_CHAT_CONTENT:
pMsg = new pb::Talk();
break;
case GameMsg::MSG_TYPE_NEW_POSTION:
pMsg = new pb::Position();
break;
case GameMsg::MSG_TYPE_BROADCAST:
pMsg = new pb::BroadCast();
break;
case GameMsg::MSG_TYPE_LOGOFF_ID_NAME:
pMsg = new pb::SyncPid();
break;
case GameMsg::MSG_TYPE_SRD_POSTION:
pMsg = new pb::SyncPlayers();
break;
default:
break;
}
// 将参数解析成消息对象内容
pMsg->ParseFromString(_stream);
}
// 序列化本地消息
std::string GameMsg::serialize(){
std::string ret;
pMsg->SerializeToString(&ret);
return ret;
}
GameProtocol.h
#include <zinx.h>
class GameChannel;
class GameProtocol
:public Iprotocol
{
std::string szLast;
public:
GameProtocol() {}
~GameProtocol() {}
/*原始数据和业务数据相互函数,开发者重写该函数,实现协议*/
virtual UserData *raw2request(std::string _szInput);
/*原始数据和业务数据相互函数,开发者重写该函数,实现协议*/
virtual std::string *response2raw(UserData &_oUserData);
/*获取处理角色对象函数,开发者应该重写该函数,用来指定当前产生的用户数据消息应该传递给哪个角色处理*/
virtual Irole *GetMsgProcessor(UserDataMsg &_oUserDataMsg);
/*获取发送通道函数,开发者应该重写该函数,用来指定当前字节流应该由哪个通道对象发出*/
virtual Ichannel *GetMsgSender(BytesMsg &_oBytes);
public:
GameChannel *m_channel = NULL;
};
GameProtocol.cpp
#include "GameProtocol.h"
#include <iostream>
#include "GameMsg.h"
#include "GameChannel.h"
#include "msg.pb.h"
using namespace std;
/*原始数据和业务数据相互函数,开发者重写该函数,实现协议*/
/**
* 输入参数是通道传来的原始报文
* 返回值是转换后的消息对象MultiMsg
* 转换方式,TCP粘包处理
*/
UserData *GameProtocol::raw2request(std::string _szInput){
// cout<<_szInput<<endl;
MultiMsg *pRet = new MultiMsg();
// 将新的报文直接添加到缓存里
szLast.append(_szInput);
while(1)
{
// 当前缓存的报文不够,后续还有本次就没有处理的必要
if(szLast.size() < 8)
break;
// 在前四个字节中读取消息内容长度
int iLength = 0;
iLength |= szLast[0] << 0;
iLength |= szLast[1] << 8;
iLength |= szLast[2] << 16;
iLength |= szLast[3] << 24;
// 中四个字节读取类型id
int id = 0;
id |= szLast[4] << 0;
id |= szLast[5] << 8;
id |= szLast[6] << 16;
id |= szLast[7] << 24;
// 通过读到的长度判断后续报文是否合法
✹ if(szLast.size() -8 <iLength)
{
// 本条报文还没够,啥都不干
break;
}
// 构造一条用户请求
GameMsg *pMsg = new GameMsg((GameMsg::MSG_TYPE)id,szLast.substr(8,iLength));
pRet->m_Msgs.push_back(pMsg);
// 弹出已经处理成功的报文
szLast.erase(0,8+iLength);
}
for (auto single : pRet->m_Msgs)
{
cout<<single->pMsg->Utf8DebugString()<<endl;
}
pb::Talk *pmsg = new pb::Talk();
pmsg->set_content("hello");
GameMsg *pGameMsg = new GameMsg(GameMsg::MSG_TYPE_CHAT_CONTENT,pmsg);
ZinxKernel::Zinx_SendOut(*(pGameMsg),*this);
return pRet;
}
/**
*参数来自业务层,待发送的消息
*返回值转换后的字节流
* */
std::string *GameProtocol::response2raw(UserData &_oUserData){
int iLength = 0;
int id = 0;
std::string MsgContent;
GET_REF2DATA(GameMsg,oOutput,_oUserData);
id = oOutput.enMsgType;
MsgContent = oOutput.serialize();
iLength = MsgContent.size();
auto pret = new std::string();
pret->push_back((iLength >> 0) & 0xff);
pret->push_back((iLength >> 8) & 0xff);
pret->push_back((iLength >> 16) & 0xff);
pret->push_back((iLength >> 24) & 0xff);
pret->push_back((id >> 0) & 0xff);
pret->push_back((id >> 8) & 0xff);
pret->push_back((id >> 16) & 0xff);
pret->push_back((id >> 24) & 0xff);
pret->append(MsgContent);
return pret;
}
/*获取处理角色对象函数,开发者应该重写该函数,用来指定当前产生的用户数据消息应该传递给哪个角色处理*/
Irole *GameProtocol::GetMsgProcessor(UserDataMsg &_oUserDataMsg){
return nullptr;
}
// 返回数据发送的通道
Ichannel *GameProtocol::GetMsgSender(BytesMsg &_oBytes){
return m_channel;
}
main.cpp
#include "GameChannel.h"
#include "GameMsg.h"
#include "msg.pb.h"
int main()
{
pb::SyncPid *pmsg = new pb::SyncPid();
pmsg->set_pid(1);
pmsg->set_username("test");
GameMsg gm(GameMsg::MSG_TYPE_LOGIN_ID_NAME,pmsg);
auto output = gm.serialize();
for (auto byte : output)
{
printf("%02X ",byte);
}
puts("");
char buff[] = {0x08,0x01,0x12,0x04,0x74,0x65,0x73,0x74};
std::string input(buff,sizeof(buff));
auto ingm = GameMsg(GameMsg::MSG_TYPE_LOGIN_ID_NAME,input);
std::cout<<dynamic_cast<pb::SyncPid *> (ingm.pMsg)->pid() <<std::endl;
std::cout<<dynamic_cast<pb::SyncPid *> (ingm.pMsg)->username() <<std::endl;
ZinxKernel::ZinxKernelInit();
// 添加监听通道
ZinxKernel::Zinx_Add_Channel(*(new ZinxTCPListen(8899,new GameConnFact())));
ZinxKernel::Zinx_Run();
ZinxKernel::ZinxKernelFini();
return 0;
}
浙公网安备 33010602011771号