Netty 通訊協(xié)議設(shè)計概要
1. 前言
上節(jié)內(nèi)容,我們主要介紹了 Netty 的粘包和拆包問題,并且大致介紹了 Netty 提供的常見拆包器,分別是固定長度拆包器、行拆包器、分隔符拆包器、基于長度域拆包器,但是它們只是相對簡單的協(xié)議,也就是說無法滿足復雜的業(yè)務(wù)場景,因此,我們可以通過自定義協(xié)議的方式去解決 TCP 的粘包和拆包問題。
2. 了解什么是協(xié)議
首先,我們大概了解什么是協(xié)議,協(xié)議可以把它認為是一種規(guī)則而不是技術(shù),約束客戶端和服務(wù)端之間通訊,數(shù)據(jù)組裝和拆分的一種規(guī)范??蛻舳税惭b某種規(guī)范去組裝數(shù)據(jù),把數(shù)據(jù)傳輸給服務(wù)端,服務(wù)端再安裝這種規(guī)范拆解數(shù)據(jù),那么這就是一種協(xié)議,可以根據(jù)實際業(yè)務(wù)區(qū)指定符合自身的協(xié)議,其實基于 Netty 去制定的私有協(xié)議,我個人接觸過的是傳輸車輛 GPS 數(shù)據(jù)的 809 協(xié)議,在和 GPS 服務(wù)器通訊時,必須按照該協(xié)議去進行封裝和解析數(shù)據(jù),否則通訊異常。
其實,類似的規(guī)則還有很多,從開發(fā)的角度來說,都是各種規(guī)則和約束,比如說:前面提到的序列化技術(shù),序列化其實就是把數(shù)據(jù)按照某種規(guī)則去轉(zhuǎn)換成 byte 數(shù)字,而反序列化就是按照這種規(guī)則再去把字節(jié)流轉(zhuǎn)換成對應(yīng)的類型數(shù)據(jù)。這些都是基于某種規(guī)則的基礎(chǔ)上,使用技術(shù)的手段去封裝的結(jié)果。
3. 通訊協(xié)議
3.1 協(xié)議架構(gòu)
思路架構(gòu)圖:
首先,我們先來了解協(xié)議在整個通訊當中的扮演的角色,如下圖所示:
如上圖所示,客戶端和服務(wù)端之間的通訊流程:
客戶端發(fā)送數(shù)據(jù)
- 客戶端先把一個對象序列化成字節(jié)流;
- 然后把字節(jié)流根據(jù)協(xié)議把字節(jié)流組裝好;
- 最后轉(zhuǎn)換成二進制傳輸?shù)骄W(wǎng)絡(luò)。
服務(wù)端接受數(shù)據(jù)
- 從網(wǎng)絡(luò)中讀取二進制數(shù)據(jù)到本地的緩沖區(qū);
- 根據(jù)協(xié)議的規(guī)則讀取指定數(shù)據(jù),并且識別是否是完整的數(shù)據(jù)包;
- 如果是完整的數(shù)據(jù)包,則轉(zhuǎn)換成實體對象。
由此可見,協(xié)議主要是管理字節(jié)流格式的一種規(guī)則,如果把協(xié)議環(huán)節(jié)去掉,那么服務(wù)端就無法知道字節(jié)流的結(jié)束位置。
3.2 協(xié)議設(shè)計
協(xié)議介紹:
- 協(xié)議標識符,以一個固定數(shù)作為標識符,占用 4 個字節(jié),主要目的是用來識別協(xié)議的開頭,只要是以該標識開頭的協(xié)議則進行處理,否則不處理。主要目的是提高處理性能問題,如果隨便一個請求都需要進行處理,但是最終處理起來發(fā)現(xiàn)協(xié)議格式不對,拋異常,肯定會影響系統(tǒng)性能;
- 數(shù)據(jù)長度,占用 4 個字節(jié),標識數(shù)據(jù)的真實長度,獲取到該值后,往后讀取指定長度的數(shù)據(jù)即可。主要目的是防止粘包和拆包安全性問題;
- 指令,協(xié)議是某個應(yīng)用所有的業(yè)務(wù)公用的一種規(guī)則,那么應(yīng)該如何區(qū)分是哪種業(yè)務(wù)呢?這里主要通過指令來進行區(qū)分;
- 數(shù)據(jù),這部分存儲的是真實的數(shù)據(jù)。
這算是比較簡單,并且常用的設(shè)計思路,主要和 Netty 內(nèi)置的基于長度域拆包器類似,基本上都是有一個字段是用來存儲真實的數(shù)據(jù)長度,這樣才能準確的讀取數(shù)據(jù)的完整內(nèi)容。當然,還可以在該設(shè)計基礎(chǔ)上加上更多的字段,比如:使用的序列號技術(shù)、協(xié)議版本號等等。
3.3 技術(shù)棧說明
相信到這里,大家對協(xié)議基本上有一個簡單的認識了,其實協(xié)議并不難,它只是一個約束而已,那么我們?nèi)绾瓮ㄟ^技術(shù)的實現(xiàn),讓協(xié)議生效呢?主要的核心思想如下:
- 序列化和發(fā)序列話技術(shù),這個在 Netty 的編解碼的時候已經(jīng)講過,序列化可以把對象轉(zhuǎn)化成字節(jié)流,反序列化可以把字節(jié)流轉(zhuǎn)換成對象;
- 字節(jié)容器(字節(jié)緩沖區(qū)),必須按照協(xié)議的字段順序往字節(jié)容器里面存放對應(yīng)的字節(jié)內(nèi)容,然后把整個容器寫到網(wǎng)絡(luò)當中。這樣數(shù)據(jù)才能按照順序進行傳輸,服務(wù)端才能按照順序進行數(shù)據(jù)的讀取和處理。
4. 小結(jié)
本節(jié)主要介紹協(xié)議的思想,它就是一種規(guī)則,客戶端和服務(wù)端必須需要遵守的規(guī)則,才能保證數(shù)據(jù)的安全性。其次,講解了協(xié)議在客戶端和服務(wù)端通訊當中所扮演的角色,如果沒有協(xié)議,那么服務(wù)端一直讀取字節(jié)流,根本無法知道數(shù)據(jù)的完整性。最后,大概介紹了協(xié)議設(shè)計的大概思想,主要核心字段有四個,分別是協(xié)議標識符、數(shù)據(jù)長度、指令、數(shù)據(jù),這四個字段是滿足協(xié)議的基本元素,可以根據(jù)實際業(yè)務(wù)再進行擴展字段。