Netty實現自定義通信協議

概述

在網絡編程中,無論使用netty還是其它的socket通訊框架,都是通過TCP或UDP傳輸二進制流。發送方把要發送的對象轉化成二進制流發送出去;接收方把接收到的二進制流轉化爲對象進行處理。
爲了能讓接收方和發送方能對同一個二進制流有相同的認識,雙方必須提前約定好一個協議,即對象如何轉化爲二進制流,二進制流如何轉化爲對象,這樣通信雙方纔不會產生誤解。

自定義通信協議

easy-im 項目中,定義如下通信協議:


魔數:4字節,一般爲固定值,本項目中使用0x88888888。一般我們的應用於某個端口對外開放,爲了防止該端口被意外調用,我們可以在收到報文後,取前4個字節與魔數比對,如果不相同,則直接拒絕並關閉連接。

版本號: 1字節,一般是預留字段,爲了支持協議升級(這種情況極少出現)。

序列化算法:1字節,表示如何將java對象轉化爲二進制數據,以及如何反序列化。

指令:1字節,表示該消息的意圖,如私聊、羣聊、登錄等。最多支持256種指令。

數據長度:4字節,表示該字段後數據部分的長度。

數據:具體數據的內容。每種指令對應的數據是不同的。

序列化算法

本項目爲了簡單起見,使用json序列化算法。將Java對象轉換成json字符串,再轉化爲二進制數據,代碼如下:
序列化器接口

Json序列化器
首先定義Serializer接口,serialize方法用於將對象序列化爲二進制數據,deSerialize方法用於將二進制反序列化成對象。getSerializerAlgorithm方法返回序列化算法。
JsonSerializer是Serializer的實現。

指令設計

定義Packet類,作爲所有指令的基類,其中getCommand方法爲抽象方法,需由子類實現,返回具體的指令類型。


登錄指令如下,登錄時需要發送userId,useName,password等信息,以及指令command:


指令類型如下:

編解碼實現

定義好序列化算法和指令之後,就可以進行編解碼的實現了。編碼即將通信包轉化爲二進制;解碼即將二進制轉化爲通信包。

編碼過程比較簡單,代碼如下,參照註釋即可明白,ByteBuf裏即是最後要發送的二進制數據:

這裏暫時跳過魔數和版本校驗,獲取到序列化算法、指令、數據長度和數據內容。根據指令我們可以知道請求類型是什麼(如登錄請求LoginRequestPacket、羣聊請求GroupChatRequestPacket),根據序列化算法,將數據內容反序列化成目標對象,解碼結束。

可以看到,編碼與解碼是相反的過程。

總結

基於netty作爲網絡通信基礎組件時,我們必須要做如下幾個步驟:

  • 定義通訊協議,通信雙方需對此協議有一致的理解;
  • 編碼,將Java對象轉化爲二進制;
  • 解碼,將二進制轉化爲Java對象;
  • 業務處理

如果採用http、websocket等公有協議通信,netty提供了許多類可以實現步驟1,2,3,無需我們編碼實現,只需調用相應的類和方法即可。

項目地址:https://github.com/sunnick/easy-im

參考文檔:
《netty入門實戰:仿寫微信IM及時通訊系統》

歡迎關注公衆號:程序員順仔
在這裏插入圖片描述

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