多人在線鬥地主遊戲開發——自定義TCP網絡通信協議包格式

    什麼叫做通信協議?爲什麼制定通信協議? 怎麼制定通信協議? 不知道大家有沒有迷茫過這個問題,反正我是有的,,,

    想我在剛接觸網絡編程的時候,是linux下用socket懵懵懂懂地按照pdf書籍上的代碼敲了個非常簡單的C/S架構的 聊天程序,

Client端發一個字符串,服務器接收到打印後,再返回一個字符串,敲完了後,自己運行着這個‘很牛 很高大上’的程序,玩得不亦樂乎,同時腦子裏迸發了一大堆的想法,終於會網絡編程了,之前寫的程序只能在本地運行,現在應該可以利用socket寫出網絡通信的程序了,我可以把自己寫的‘遊戲’、‘軟件’發給遠在他方的朋友,一起聯網運行(事實上,想多了,理想是豐滿的,現實是異常骨感的)。

      經過了一番思考,動手利用socket寫了個簡單的發送文件的程序,Client發送FileName至Server, Server收到後返回“OK”,然後Client開始讀取文件數據,並send.

      其實這裏的Client發送FileName,服務器返回OK,  可以說就是所謂的通信協議了,只不過是簡陋得有點可憐的通信協議。

       通信協議,實際上就是一種通信的約定,我發什麼數據給你,你根據我發的這個數據,要返回什麼數據給我,例如下面這個不是很好的例子:

           

 

       上面就是一個簡單登錄協議, 開發者在動手敲代碼之前,就已經規定了Client會發送哪些數據,服務器要怎樣響應數據,如Client 發送‘用戶名和密碼數據,驗證登錄’,  Server收到後根據協議,就知道這條指令是什麼意思,需要我做什麼事情,然後我做完了之後該怎麼返回結果給Client, 按照上面定好的協議,Server如果驗證成功,則返回B 代表的指令,  如果驗證失敗,則返回C代表指令,而且只能返回這兩者其一,不能返回其他數據, 一般協議之外的其他消息數據應該被程序認爲是不合法的數據,無效的數據。

      如果Server返回的是B, 那麼Client就知道賬戶密碼正確,登錄成功了,如果B的指令數據中含有Hall Server(大廳服務器)的IP和Port的話, Client就知道,它可以連接遊戲大廳了

      如果Server返回的是C, 那麼Client就知道驗證失敗了,就會做失敗的處理。

      所以,通信協議就是一個約定,一個規定,一份對應的請求和響應指令,我發送什麼指令給你,你根據我的指令判斷 你需要做什麼操作,然後根據協議約定的返回結果的數據格式,返回結果給我,我收到你的返回,根據協議上的約定,我就知道接下來我該怎麼去做。

       那到這裏了,大家可能想問了,在我不知道什麼是通信協議的時候,我就已經在用着了,就像上面發送文件的程序一樣,我發個字符串給服務器,服務器給我返回一個字符串就行了,那我就按照這樣就可以了,何必要再花時間費腦子去制定一個協議呢?

       那不知道大家在寫socket TCP程序的時候,有沒有遇到過粘包的問題,那時候我和兩個同學,寫一個局域網鬥地主遊戲,在tcp通信的時候,經常會遇到這個問題,當Client有連續發送“OK”、“bige”兩條消息給服務器的時候,理想狀態是,服務器接收到“OK”,然後做相應處理,然後接收到“bige”,再做相應處理, 而實際上呢,服務器接收到的是“OKbige”, 然後導致服務器不知道這是條什麼命令,然後無任何相應,甚至出錯。

        socket Tcp是流式地發送數據,沒有上一條數據和下一條數據之間的界限,屬於是水乳交融,無縫連接的。

        那麼該怎麼解決連包的問題呢? 

        我們可以通過制定一份通信協議來解決連包問題,同時也大大降低程序通信的混亂性,提升代碼可讀性和健壯性。

        通過閱讀了多個開源遊戲服務器的網絡通信模塊,以及一些文檔資料,我大概確定怎樣制定一份協議,(我知道這可能不是一份完美的協議格式,可能存在一些問題,如果有何建議的話,恭請不吝指教)如下:

        一條協議數據應該由三部分組成:協議頭  + 指令數據 + 校驗碼

            1)協議頭:    必須固定大小爲多少字節,例如:

    

            有了協議頭後,解析協議數據的時候,先接受10個字節的協議頭數據,然後在協議頭中解析指令類型,再解析指令數據長度length,然後再讀取length個字節的數據,這length個字節的數據就是一條單獨的指令,後面還有數據的話,就等下次解析了。

           2)指令數據: Client與Server之間具體的請求響應交互操作

           3)校驗碼:

                校驗碼一是校驗協議包的完整性,二是校驗協議包的合法性,防止有其他人惡意 發送消息給Server,造成Server的資源消耗等

        所以完整的協議格式應該如下(序號可以視情況選擇用不用):

       發送方 根據此協議格式組織 協議包,發送給接收方,接收方接收後的解析步驟:

              1)讀取10字節協議頭, 解析出指令長度len

              2)接着後面讀取len個字節的數據,此時,已經讀取了len+10字節數據,剛好就是一個完整的協議包

              3)將前len+10-2個字節數據,進行校驗算法的校驗,生成出兩字節的校驗位

              4)將生成的校驗位與接收到的協議包中的校驗位進行比較,如果相同則,指令正確,如果不同,則校驗失敗,數據異常,可以選擇丟掉數據等處理

              5)校驗成功後,根據協議包中的指令類型,將指令分發到對應功能的接口模塊,進行處理。

        此外,通常情況下,爲了防止tcp通信的協議包被抓包破解,泄漏程序的業務流程,造成極大的安全隱患,在發送組織好的協議包之前,還會用加密算法,將指令數據進行加密,然後再發送至接收端,接收端,先進行解密,再效驗數據。

 

        網絡通信中用到的通信格式,還有Json、XML、谷歌的protobuf等等

 

 

 

 

 

 

 

 

 

 

 

 

 

 

   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章