近些天写了一些游戏逻辑,发现项目组在写C/S网络消息包的时候,根据作用的不同,定义了很多的不同的消息格式。比如,所有的消息包都继承自下面这个基本结构。
struct SPacket
{
byte bType;
} 然后,添加物品的消息包,设置成员状态的消息包都分别定义了不同的结构。
struct SAddItemPacket : public SPacket
{
SItem item;
SAddItem()
{
bType = STC_AddItem;
}
}
struct SSetMemberPacket : public SPacket
{
byte bStatus;
SSetMemberPacket()
{
bType = STC_SetMember;
}
} 随着项目填充越来越多的内容,消息包的类型越来越多,不能避免吗?
晚上睡觉的时候,想着如果把网络引擎引入项目之类的会怎样呢?然后想到看过的ICE教程,就大概的思考类似ICE这样的通用RPC大概是怎样实现的。
RPC由服务对象以及其客户端的表现——代理对象构成。客户端代码中,调用了代理对象的某个接口,就会导致其服务对象执行该接口的实现,使得客户端看起来就像是执行本地调用一样。
因此RPC的直观概念是接口调用的网络映射,核心内容是:如何将客户端调用(某个对象的)某个接口的这一行为进行通用的序列化?
对调用行为序列化和对对象序列化是不同的,而且RPC的目标是对任何类型的接口调用行为都能有一个通用的序列化方案。
我们要调用某个接口的时候,下达的命令通常是,“某某类型的,那个谁,把你的那个名叫什么的方法调用一下,参数是这些,a,b,c,d”。这样我们就进行了一次接口调用。因此,调用行为最基本的序列化方法是,类型ID+方法ID+对象ID+参数。将这些数据加成起来,就能作为一个调用消息包由客户端发向服务器,服务器处理RPC管理的对象将会根据类ID,对象ID找到服务对象,然后根据方法ID,找到方法,并能根据方法知道有几个参数,以及参数的类型,最终执行调用。
因此,我此时觉得,像项目组现在定义的多种大小不变,种类繁多的消息格式,其实都可以用一个能序列化调用行为的消息包来解决。该消息包的结构是:
class SPacket
{
WORD wClassID;
WORD wMemberFuncID;
BYTE bParam[PACKET_PARAM_BUFFER_SIZE];
QWORD qwBufferOffset = 0;
public:
SPacket(WORD par_wClassID,WORD par_FuncID, DWORD dwObjID);
SPacket(WORD par_wClassID,WORD par_FuncID, QWORD qwObjID);
template<typename T>
bool Push(T value)
{
memcpy(bParam + qwBufferOffset,value,sizeof(T);
qwBufferOffset += sizeof(T);
}
bool Pop(T &value)
{
T *pValue = static_cast<T*>(bParam + qwBufferOffset)
value = *pValue;
qwBufferOffset += sizeof(T);
}
QWORD Size() const
{
return sizeof(wClassID) + sizeof(wMemberFuncID) + qwBufferOffset;
}
}; 参数从左到右装入消息包,相应的也是从左到右从消息包取出,来配合服务器端执行相应的函数。
posted on 2007-01-26 23:26
LOGOS 阅读(2488)
评论(12) 编辑 收藏 引用