2.消息包的处理(protobuffer)
二、
1.先看看框架代码目录结构:
2.以下以loginGroup登录组业务模块为例,其他业务模块结构都基本相同。
3.首先从network网路模块核心说起。
核心类类图如下:
4.stFrontServer初始化调用listenAt,参数IOhandle最终传到tcpSocket类中,在监听doRead()收到客户端消息解包时,通过调用IOhandle的postNewMsg(constnetLinkPtrpNetLink,uint64uid,uint32cmd,constmsgBufPtrpBuff),从而达到执行业务逻辑的消息包的回调。boolstNetWork::listenAt(uint16port,stIOHandler*pHandler,boolopenPing,conststd::stringlinkType){
autoiterF=mAccepterMap.find(port);
if(iterF!=mAccepterMap.end()){
log_error("listenport[%u]faild,porthaslistend",port);
returnfalse;
}
if(NULL==pHandler){
log_error("listenport[%u]msgHandlerisNULL",port);
returnfalse;
}
acceptPtrpAccept(newstAccepter(mIos,pHandler,linkType,openPing));
if(!pAccept-listen(port)){
log_error("listenport[%u]faild!",port);
returnfalse;
}
mAccepterMap[port]=pAccept;
returntrue;
}
5.再具体看看stAccpter具体做了啥,socket实例化和bind,connect,循环监听等
boolstAccepter::listen(uint16listenPort){
try{
boost::asio::ip::tcp::resolverres(mIos);
boost::asio::ip::tcp::endpointep(boost::asio::ip::address::from_string("0.0.0.0"),listenPort);
mAcceptor.open(ep.protocol());
mAcceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
mAcceptor.bind(ep);
mAcceptor.listen();
doAccept();
log_debug("[%s]listenat:%usuccess",mIOHandler-getName().c_str(),listenPort);
}catch(boost::system::system_errorec){
log_error("[%s]listenat:%ufaild,error:[%s]",mIOHandler-getName().c_str(),listenPort,ec.what());
returnfalse;
}
returntrue;
}
voidstAccepter::doAccept(){
mAcceptor.async_accept(mNewSocket,mNewEndPoint,mStrand.wrap([this](boost::system::error_codeec)
{
if(ec==boost::asio::error::operation_aborted){
return;
}
if(!mAcceptor.is_open()){
log_error("acceptisnotopen");
return;
}
do{
if(ec){
log_error("accepterror");
break;
}
netLinkPtrpNetLink=newNetLink(mLinkType,mIos,mNewSocket,mNewEndPoint,mIOHandler,this);
if(NULL!=pNetLink){
onConnect(pNetLink);
pNetLink-onConnect(true);
}
}while(0);
doAccept();
})
);
}
6,在4中,要做消息的回调,使用的是函数指针的方式。在每一个业务模块(继续IOhandle)里肯定会有多个消息号要处理。这时就stMessageHandler上场了,每一个业务模块里会申明一个。stMessageHandler它里面有个mapuint16,msgHandlerFuncmFuncMap,key为消息号id,value为函数指针。在业务类stClientMsgHandler中实现父类的onNewMsg接口里则调用这个函数指针,从而执行完毕,完成一个从收包到处理消息逻辑的完整流程。
7.这里以客户端发送连接消息为例,并体会下宏的使用.在类stClientMsgHandler中:
virtualvoidonNewMsg(constnetLinkPtrpSocket,uint64uid,uint32cmd,constmsgBufPtrbuff){
//...
mMsgHandle.handleMsg(pSocket,uid,cmd,buff)
}
virtualboolinitMsgHandle(){
REGISTER_CMD(cl2fr_connect_request)//注册,展开如下//mMsgHandle.regFunc(op_cmd::cl2fr_connect_request,handle_cl2fr_connect_request);
}
stMessageHandlermMsgHandle;
DEC_HANDLE_MSG(cl2fr_connect_request);//.h申明,展开如下
//staticboolhandle_cl2fr_connect_request(constnetwork::netLinkPtrs,uint64uid,constnetwork::msgBufPtrmsgBuf);
DEF_HANDLE_BEGIN(stClientMsgHandler,server,cl2fr_connect_request){//.cpp定义
stClientManager::onSDKLogin(s,msg.account_no(),msg.platform(),msg.opid());
}
//展开如下
boolstClientMsgHandler::handle_cl2fr_connect_request(constnetLinkPtrs,uint64uid,constmsgBufPtrmsgBuf){
UNWRAP_MSG(proto::client::cl2fr_connect_request);
stClientManager::onSDKLogin(s,msg.account_no(),msg.platform(),msg.opid());
}
8.下面看看宏的定义:
#defineREGISTER_CMD_LINK(handleName,name)\
handleName::regHandleFunc(op_cmd::name,handle_##name);
#defineREGISTER_CMD(name)\
mMsgHandle.regFunc(op_cmd::name,handle_##name);
#defineDEC_HANDLE_MSG(name)\
staticboolhandle_##name(constnetwork::netLinkPtrs,uint64uid,constnetwork::msgBufPtrmsgBuf);
#defineDEF_HANDLE_BEGIN(type,side,name)\
booltype::handle_##name(constnetwork::netLinkPtrs,uint64uid,constnetwork::msgBufPtrmsgBuf){\
UNWRAP_MSG(proto::side::name);\
if(true==gLogger.mLogContral.decodeMsg){\
log_msg("msgdecode[%s]",msg.Utf8DebugString().c_str());\
}
9.注意解包的宏:UNWRAP_MSG
//解包
templatetypenameT_MSG
staticboolunWrapMsg(T_MSGmsg,constmsgBufPtrbuf){
if(buf-isCompress()){
charuzBuf[PACKET_MAX_SIZE]={0};
bzero(uzBuf,sizeof(uzBuf));
uLongfbufSize=PACKET_MAX_SIZE;
int32retcode=un