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;
}

posted on 2021-07-30 10:45  lodger47  阅读(434)  评论(0)    收藏  举报

导航