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及時通訊系統》

歡迎關注公眾號:程序員順仔和他的朋友們


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM