近些天寫了一些游戲邏輯,發現項目組在寫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由服務對象以及其客戶端的表現——代理對象構成??蛻舳舜a中,調用了代理對象的某個接口,就會導致其服務對象執行該接口的實現,使得客戶端看起來就像是執行本地調用一樣。
因此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 閱讀(2497)
評論(12) 編輯 收藏 引用