如果你正在做的插座級編程,那麼不管你有多少端口打開每個消息類型,你仍然需要有某種形式的頭。即使它只是消息其餘部分的長度。話雖如此,將一個簡單的頭部和尾部結構添加到消息很容易。我認爲只需處理客戶端的一個端口就簡單多了。
我相信現代MMORPG遊戲(甚至舊的)有服務器的兩個層次。驗證您爲付費客戶端的登錄服務器。一旦確認這些信息會傳遞給包含所有遊戲世界信息的遊戲服務器。即便如此,這仍然只需要在客戶端有一個套接字開放,但並沒有禁止擁有更多。
此外大多數MMORPGS也加密所有的數據。如果你將這寫成練習以獲得樂趣,那麼這並不重要。
設計/一般書面協議,這裏的事情,我擔心:
端模式
是客戶端和服務器始終保證有相同的字節順序。如果沒有,我需要在我的序列化代碼中處理。處理永恆性有多種方式。
- 忽略它 - 顯然,一個壞的選擇
- 指定協議的字節序。這是舊協議所做/做的,因此術語網絡訂單總是大端的。它並不重要,只是你指定了哪一個或哪一個。如果你不使用大型的endianess,一些頑固的舊的網絡程序員會站起來,但是如果你的服務器和大多數客戶端是小端的話,那麼除了通過使協議變成大端,你實際上不會爲自己購買任何東西。
- 標記每個頭中的Endianess - 您可以添加一個cookie來告訴您客戶端/服務器的永久性,並根據需要對每個消息進行相應轉換。加班!
- 使你的協議不可知 - 如果你把所有的東西都發送成ASCII字符串,那麼endianess是無關緊要的,但是效率更低。
在4我通常會選擇2,並指定endianess是大多數客戶端,現在的日子將是小端。
向前和向後兼容
是否協議需要是向前和向後兼容。答案几乎總是肯定的。在這種情況下,這將決定我如何設計整個協議的版本控制,以及如何創建每個單獨的消息來處理實際上不應該成爲版本控制一部分的微小更改。你可以通過這種方式來使用XML,但是你會失去很多效率。
對於整體版本,我通常設計一些簡單的東西。客戶端發送版本信息,指定它說X.Y版本,只要服務器支持該版本,它就會發回一條確認客戶端版本的消息,並且所有內容都會繼續前進。否則它會暫停客戶端並終止連接。
對於每一個消息,你有類似如下:
+-------------------------+-------------------+-----------------+------------------------+
| Length of Msg (4 bytes) | MsgType (2 bytes) | Flags (4 bytes) | Msg (length - 6 bytes) |
+-------------------------+-------------------+-----------------+------------------------+
長度明顯告訴你的消息是多久,不包括本身的長度。 MsgType是消息的類型。對於這個,只有兩個字節,因爲65356有很多應用程序的消息類型。這些標誌讓你知道消息中的序列化內容。這個字段與長度結合在一起就是你的向前兼容性和向後兼容性。
const uint32_t FLAG_0 = (1 << 0);
const uint32_t FLAG_1 = (1 << 1);
const uint32_t FLAG_2 = (1 << 2);
...
const uint32_t RESERVED_32 = (1 << 31);
那麼你的反序列化的代碼可以執行類似如下:
uint32 length = MessageBuffer.ReadUint32();
uint32 start = MessageBuffer.CurrentOffset();
uint16 msgType = MessageBuffer.ReadUint16();
uint32 flags = MessageBuffer.ReadUint32();
if (flags & FLAG_0)
{
// Read out whatever FLAG_0 represents.
// Single or multiple fields
}
// ...
// read out the other flags
// ...
MessageBuffer.AdvanceToOffset(start + length);
這可以讓你添加新領域到底的消息,而不必修改整個協議。它還確保舊服務器和客戶端將忽略他們不知道的標誌。如果他們必須使用新的標誌和字段,那麼您只需更改總體協議版本。
使用框架的工作或沒有
有我會考慮使用的業務應用程序的各種網絡框架。除非我有特殊的需求,否則我會選擇標準框架。在你的情況下,你想學習套接字級編程,所以這個問題已經爲你解答。
如果你確實使用了框架,請確保它解決了上述兩個問題,或者至少在你需要在這些區域中定製它時阻礙你。
我是與第三方
處理在很多情況下,你可能會處理與第三方服務器/客戶端需要與溝通。這意味着一些場景:
- 他們已經有一個協議定義 - 只需使用他們的協議。
- 您已經定義了一個協議(他們願意使用它) - 再次簡單地使用定義的協議它們使用標準框架(基於WSDL等) - 使用框架。
- 任何一方都沒有定義協議 - 嘗試根據手邊的所有因素(我在這裏提到的所有參數)以及他們的能力水平(至少據您所知)來確定最佳解決方案。無論如何確保雙方都同意並理解協議。從經驗來看,這可能是痛苦的或愉快的。這取決於你與誰一起工作。
無論哪種情況,您都不會與第三方合作,所以這只是爲了完整性而添加的。
我感覺好像我可以寫更多的關於這個,但它已經很長。我希望這會有所幫助,如果您有任何具體問題,請向Stackoverflow詢問。
一個編輯回答knoopx的問題:
你能說出任何這些網絡框架嗎? – knoopx 2009-08-10 12:56:27