kbe服务端笔记
main
看起来似乎所有的组件都有一个这样的宏(KBENGINE_MAIN)来包裹main函数
intKBENGINE_MAIN(intargc, char* argv[])
{
ENGINE_COMPONENT_INFO&info = g_kbeSrvConfig.getXXX();
returnkbeMainT<XXX>(argc, argv, YYY, info.externalPorts_min,
info.externalPorts_max, info.externalInterface, 0, info.internalInterface);
}
这个宏展开是这样子:
kbeMain(intargc, char* argv[]); \
intmain(intargc, char* argv[]) \
{ \
loadConfig(); \
g_componentID = genUUID64(); \
parseMainCommandArgs(argc, argv); \
char dumpname[MAX_BUF] = {0}; \
kbe_snprintf(dumpname, MAX_BUF, "%"PRAppID, g_componentID); \
KBEngine::exception::installCrashHandler(1, dumpname); \
intretcode = -1; \
THREAD_TRY_EXECUTION; \
retcode = kbeMain(argc, argv); \
THREAD_HANDLE_CRASH; \
returnretcode; \
} \
稍微整理一下之后main函数看起来很像是这个样子:
intkbeMain(intargc, char* argv[]);
intmain(intargc, char* argv[])
{
loadConfig();
g_componentID = genUUID64();
parseMainCommandArgs(argc, argv);
chardumpname[MAX_BUF] = {0};
kbe_snprintf(dumpname, MAX_BUF, "%"PRAppID, g_componentID);
KBEngine::exception::installCrashHandler(1, dumpname);
intretcode = -1;
THREAD_TRY_EXECUTION;
retcode = kbeMain(argc, argv);
THREAD_HANDLE_CRASH;
return (retcode);
}
intkbeMain(intargc, char* argv[])
{
ENGINE_COMPONENT_INFO&info = g_kbeSrvConfig.getXXX();
return kbeMainT<XXX>(argc, argv, YYY, info.externalPorts_min, info.externalPorts_max, info.externalInterface, 0, info.internalInterface);
}
基本可以理解为每个组件的main函数流程都是一样的,只是在特化kbeMainT时所给参数不一样。
ServerConfig:
ServerConfig涉及到服务端每个组件的各种配置选项,比如数据库访问。它的构造在组件名.cpp中,比如loginapp就在loginapp.cpp,machine就在machine.cpp中,loginapp的如下(server/loginapp/loginapp.cpp):
ServerConfigg_serverConfig;
KBE_SINGLETON_INIT(Loginapp);
它的初始化(配置)工作主要由loadConfig接口完成,如下(lib/server/kbemain.h):
inlinevoidloadConfig()
{
Resmgr::getSingleton().initialize();
// "../../res/server/kbengine_defs.xml"
g_kbeSrvConfig.loadConfig("server/kbengine_defs.xml");
// "../../../assets/res/server/kbengine.xml"
g_kbeSrvConfig.loadConfig("server/kbengine.xml");
}
Resmgr:
Resmgr负责管理kbe的所有资源管理,比如资源路径,环境变量。Resmgr的构造地方如下(lib/network/fixed_messages.cpp):
FixedMessages::FixedMessages():
_infomap(),
_loaded(false)
{
newResmgr();
Resmgr::getSingleton().initialize();
}
我们可以理解为FixedMessages构造的时候Resmgr就构造了。
Resmgr的初始化(配置)工作主要由initialize接口完成,代码如上。
FixedMessages:
FixedMessages存储所有固定消息(有显示制定id的消息,当然,这并不表示非固定消息就没有id,也是有的,只是不是显示制定的)。
它的构造地方如下(lib/network/message_handler.cpp):
MessageHandlers::MessageHandlers():
msgHandlers_(),
msgID_(1),
exposedMessages_()
{
g_fm = Network::FixedMessages::getSingletonPtr();
if(g_fm == NULL)
g_fm = newNetwork::FixedMessages;
Network::FixedMessages::getSingleton().loadConfig("server/messages_fixed.xml");
messageHandlers().push_back(this);
}
意即MessageHandlers构造的时候,如果它还没构造,那就构造。
它的初始化(配置)是由loadConfig接口来完成的,代码见上。
loginapp
Loginapp组件主要用来处理账户登录/注册的业务
消息与handler映射的建立:
两次包含xxx_interface.h,实现声明和定义:
每个app组件的接口定义都在xxxapp_interface.cpp中开始,代码如下:
#include"loginapp_interface.h"
#defineDEFINE_IN_INTERFACE
#defineLOGINAPP
#include"loginapp_interface.h"
namespaceKBEngine{
namespaceLoginappInterface{
}
}
所有的戏法都是通过包含loginapp_interface.h前后定义了DEFINE_IN_INTERFACE和LOGINAPP来完成的。第一次的包含就是各种变量,类的声明(当然也有一些类是声明类时使用类inline函数定义完成了,比如MESSAGE_ARGS0/1/2……)。
我们看看loginapp_interface.h中的代码:
消息与handlers的存储
首先是这一句:
NETWORK_INTERFACE_DECLARE_BEGIN(LoginappInterface)
此句展开的话声明和定义了Network::MessageHandlers messageHandlers(记住它们都在LoginappInterface命名空间内),展开宏之后的代码看起来像这样(是的,你的眼睛是好的,没有}闭合):
声明:
namespaceLoginappInterface {
extern Network::MessageHandlers messageHandlers;
定义:
namespaceLoginappInterface {
Network::MessageHandlers messageHandlers;
消息与handle建立映射
然后是这一句:
LOGINAPP_MESSAGE_DECLARE_ARGS0(importClientMessages, NETWORK_FIXED_MESSAGE)
此句展开的话分明声明和定义了一个importClientMessagesLoginappMessagehandler0的类,这个类继承自Network::MessageHandler,这里就是实现了handle的虚函数接口;声明和定义了importClientMessagesLoginappMessagehandler0的一个名为importClientMessages的全局变量;声明和定义了importClientMessagesArgs0的类,这个类继承自Network::MessageArgs。我们一个个地分析一下:
首先展开下面的宏:
LOGINAPP_MESSAGE_DECLARE_ARGS0(importClientMessages, NETWORK_FIXED_MESSAGE)
之后是这样:
#defineLOGINAPP_MESSAGE_DECLARE_ARGS0(NAME, MSG_LENGTH) \
LOGINAPP_MESSAGE_HANDLER_ARGS0(NAME) \
NETWORK_MESSAGE_DECLARE_ARGS0(Loginapp, NAME, \
NAME##LoginappMessagehandler0, MSG_LENGTH)
展开LOGINAPP_MESSAGE_HANDLER_ARGS0(NAME)之后分别得到importClientMessagesLoginappMessagehandler0的声明和定义:
声明:
classimportClientMessagesLoginappMessagehandler0 : public Network::MessageHandler
{
public:
virtualvoidhandle(Network::Channel* pChannel, KBEngine::MemoryStream&s);
};
定义:
voidimportClientMessagesLoginappMessagehandler0::handle(Network::Channel* pChannel, KBEngine::MemoryStream&s)
{
KBEngine::Loginapp::getSingleton().importClientMessages(pChannel);
}
(handle/handler,傻傻分不清楚。。。这里的handle是xxxApp中真正用来处理这个消息的接口,而这里的handler提供一个中间层的作用,集中处理一些通用的工作,可以将耦合减少一点)
上面完成了相当于是importClientMessages消息的handler的声明和定义,下面则将这个类实例化之后添加到messageHandlers:
#defineNETWORK_MESSAGE_DECLARE_ARGS0(DOMAIN, NAME, MSGHANDLER, \
MSG_LENGTH) \
NETWORK_MESSAGE_HANDLER(DOMAIN, NAME, MSGHANDLER, MSG_LENGTH, 0)\
MESSAGE_ARGS0(NAME)
\
展开NETWORK_MESSAGE_HANDLER(DOMAIN, NAME, MSGHANDLER, MSG_LENGTH, 0)之后得到importClientMessages的handler类(importClientMessagesLoginappMessagehandler0)的名为importClientMessages的全局变量(不过欣慰的是他们都在各自的XXXInterface命名空间内)。
声明:
externconstimportClientMessagesLoginappMessagehandler0&importClientMessages;
定义:
importClientMessagesLoginappMessagehandler0* pimportClientMessages = static_cast<importClientMessagesLoginappMessagehandler0*>(messageHandlers.add("Loginapp::importClientMessages",new importClientMessagesArgs0, NETWORK_FIXED_MESSAGE, newimportClientMessagesLoginappMessagehandler0);
constimportClientMessagesLoginappMessagehandler0&importClientMessages = *pimportClientMessages;
下面的MESSAGE_ARGS0(NAME)展开后对importClientMessagesArgs0进行了声明和定义(其他它声明的时候就已经完成了全部的定义),声明的时候就是个空语句:
声明兼定义:
classimportClientMessagesArgs0 : public Network::MessageArgs
{
public:
importClientMessagesArgs0() :Network::MessageArgs() {}
~importClientMessagesArgs0() {}
staticvoidstaticAddToBundle(Network::Bundle&s)
{
}
staticvoidstaticAddToStream(MemoryStream&s)
{
}
virtual int32 dataSize(void)
{
return 0;
}
virtualvoidaddToStream(MemoryStream&s)
{
}
virtualvoidcreateFromStream(MemoryStream&s)
{
}
};
唯一需要小注意一下的就是importClientMessagesArgs0的声明(兼定义)是和importClientMessagesLoginappMessagehandler0的实例的声明和定义是错开的,因为后者实例化添加到messageHandlers的时候需要new一个importClientMessagesArgs0的实例。
流程的伪代码
稍微整理一下之后,使用LOGINAPP_MESSAGE_HANDLER_ARGSn建立一个消息到handler的映射的代码很像是这样:
声明:(第一次包含loginapp_interface.h产生的代码)
classimportClientMessagesLoginappMessagehandler0 : public Network::MessageHandler
{
public:
virtualvoidhandle(Network::Channel* pChannel, KBEngine::MemoryStream&s);
};
externconstimportClientMessagesLoginappMessagehandler0&importClientMessages;
classimportClientMessagesArgs0 : public Network::MessageArgs
{
public:
importClientMessagesArgs0() :Network::MessageArgs() {}
~importClientMessagesArgs0() {}
staticvoidstaticAddToBundle(Network::Bundle&s)
{
}
staticvoidstaticAddToStream(MemoryStream&s)
{
}
virtual int32 dataSize(void)
{
return 0;
}
virtualvoidaddToStream(MemoryStream&s)
{
}
virtualvoidcreateFromStream(MemoryStream&s)
{
}
};
定义:(定义DEFINE_IN_INTERFACE和LOGINAPP之后第二次包含loginapp_interface.h产生的代码)
voidimportClientMessagesLoginappMessagehandler0::handle(Network::Channel* pChannel, KBEngine::MemoryStream&s)
{
KBEngine::Loginapp::getSingleton().importClientMessages(pChannel);
}
importClientMessagesLoginappMessagehandler0* pimportClientMessages
= static_cast<importClientMessagesLoginappMessagehandler0*>(messageHandlers.add("Loginapp::importClientMessages",
newimportClientMessagesArgs0,
NETWORK_FIXED_MESSAGE,
newimportClientMessagesLoginappMessagehandler0);
constimportClientMessagesLoginappMessagehandler0&importClientMessages = *pimportClientMessages;
消息id:固定消息与非固定消息
要接着v0.0.3的分析继续写,回过头来要看之前写的东西说实话自己都有点难以理解。。。不过出于幸运或者努力,总算是看懂了;-(,读源代码(感觉特别是C++)本来就不是件容易的事,所以读源代码一定要做好长期战斗的准备。
上面我们分析到了,其实一个消息,就是由这样一个宏来和它的handle建立链接的:
LOGINAPP_MESSAGE_DECLARE_ARGS0(importClientMessages, NETWORK_FIXED_MESSAGE)
通过上面的分析,我们得知,实际上建立消息和handle映射,起到核心作用的接口是messageHandlers.add(xxx, xxxx),所以我们跟进去看看(lib/network/message_handler.cpp):
MessageHandler* MessageHandlers::add(std::stringihName, MessageArgs* args,
int32msgLen, MessageHandler* msgHandler)
{
if(msgID_ == 1)
{
//printf("\n------------------------------------------------------------------\n");
//printf("KBEMessage_handlers begin:\n");
}
//bool isfixedMsg = false;
FixedMessages::MSGInfo* msgInfo = FixedMessages::getSingleton().isFixed(ihName.c_str());
if(msgInfo == NULL)
{
while(true)
{
if(FixedMessages::getSingleton().isFixed(msgID_))
{
msgID_++;
//isfixedMsg = true;
}
else
{
break;
}
};
msgHandler->msgID = msgID_++;
}
else
{
msgHandler->msgID = msgInfo->msgid;
}
msgHandler->name = ihName;
msgHandler->pArgs = args;
msgHandler->msgLen = msgLen;
msgHandler->exposed = false;
msgHandler->pMessageHandlers = this;
msgHandler->onInstall();
msgHandlers_[msgHandler->msgID] = msgHandler;
if(msgLen == NETWORK_VARIABLE_MESSAGE)
{
//printf("\tMessageHandlers::add(%d): name=%s, msgID=%d, size=Variable.\n",
// (int32)msgHandlers_.size(), ihName.c_str(), msgHandler->msgID);
}
else
{
if(msgLen == 0)
{
msgHandler->msgLen = args->dataSize();
if(msgHandler->type() == NETWORK_MESSAGE_TYPE_ENTITY)
{
msgHandler->msgLen += sizeof(ENTITY_ID);
}
}
//printf("\tMessageHandlers::add(%d): name=%s, msgID=%d, size=Fixed(%d).\n",
// (int32)msgHandlers_.size(), ihName.c_str(), msgHandler->msgID, msgHandler->msgLen);
}
//if(isfixedMsg)
// printf("\t\t!!!message is fixed.!!!\n");
returnmsgHandler;
}
大意可以理解为,首先看看消息名称是不是一个固定消息,我们跟进去看看(lib/network/fixed_messages.cpp):
FixedMessages::MSGInfo* FixedMessages::isFixed(constchar* msgName)
{
MSGINFO_MAP::iteratoriter = _infomap.find(msgName);
if(iter != _infomap.end())
{
MSGInfo* infos = &iter->second;
returninfos;
}
returnNULL;
}
//-------------------------------------------------------------------------------------
boolFixedMessages::isFixed(MessageIDmsgid)
{
MSGINFO_MAP::iteratoriter = _infomap.begin();
while (iter != _infomap.end())
{
FixedMessages::MSGInfo&infos = iter->second;
if(infos.msgid == msgid)
returntrue;
++iter;
}
returnfalse;
}
固定消息
通过通读FixedMessages(fixed_message.h/.cpp)可以看到这个_infomap是在loadConfig中建立的,这个_infomap就是所谓的固定消息(fixed message)与其id的映射表。loadConfig就是检视server/messages_fixed.xml,将其中的消息名称与其id关联建立这个映射表。我们继续接着看MessageHandlers::add接口。
非固定消息
对于isFixed为假的消息(非固定消息),则为其生成一个id(随着调用add的次序依次递增),这个id是在MessageHandlers类中唯一的,而每个组件的MessageHandlers又是处于自己的命名空间内,所以当出现某个组件的非固定消息时,则会为其生成单一组件内唯一的id(但这个id并不是所有组件内唯一的)。于是可能出现这种情况,Loginapp::xxxx与Dbmgr::yyyy都是非固定消息,但他们却有着同样的消息id,此时若有其他组件发送其中任一消息给其他组件,接受消息的组件将无法识别到底是Loginapp::xxxx或者是Dbmgr::yyyy。当然,只要我们将非固定消息发送给所属的组件,则不会有问题(上例中任何组件将Loginapp::xxxx发送给loginapp都是不会出乱子的)。
dbmgr
dbmgr组件主要负责数据库相关的事务,比如:账户登录/注册事务;账户充值
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)