何爲自定義協議,實際上是相對標準協議來講的,這裏主要針對的是應用層協議;常見的標準的應用層協議如http、ftp、smtp等,若是咱們在網絡通訊的過程當中不去使用這些標準協議,那就須要自定義協議,好比咱們經常使用的RPC框架(dubbo,thrift),分佈式緩存(redis,memcached)等都是自定義協議;本文就來說講如何去自定義私有協議,在此以前咱們先考慮一下爲何要自定義協議。html
直接使用標準的協議好處是顯而易見的,我我的理解的幾點優勢:面試
既然有這麼多優勢那咱們爲何還要去自定義協議,大體出於如下幾點考慮:redis
以上只是我的的一點理解,歡迎你們補充;關於如何去自定義協議,其實能夠去多參考一些主流的標準協議或者私有協議,其實有不少共同點能夠去借鑑;下面先簡單看看那些主流的協議;算法
下面分別看看一些主流的標準協議或者私有協議都是如何去定義本身的數據結構的,對咱們有很是好的借鑑意義;數據庫
http協議你們最熟悉不過了,全稱叫超文本傳輸協議,整個請求報文能夠分爲三個部分分別是:請求行,請求報頭,請求正文;json
GET /test.html HTTP/1.1 (CRLF換行)
Accept-Encoding: gzip, deflate Content-Length: 38 Content-Encoding: gzip ...
請求包頭有不少,每個表明了各自的含義,這邊就不一一列出,咱們這裏更加關注整個報文的結構;segmentfault
這個只有在POST請求的時候纔有正文,裏面存放業務數據,好比常見的json文本串;具體正文的長度能夠根據消息頭中的Content-Length來決定;緩存
dubbo協議格式能夠直接參考官網提供的以下圖片:
看上圖其實整個協議數據包也大體分爲兩個部分:固定部分和可變部分,或者叫消息頭和消息體;
固定部分一共是4+8+4=16個字節,具體以下所示:安全
header{ Magic High = 8bit; //魔數高位 Magic Low = 8bit; //魔數低位 Req/Res = 1bit; //標識是請求或響應 2 Way = 1bit; //標記是否指望從服務器返回值 Event = 1bit; //標識是不是事件消息 Serialization ID = 5bit; //標識序列化類型 Status = 8bit; //標識響應的狀態 Request ID = 64bit; //標識惟一請求 Data Length = 32bit; //序列化後的內容長度 }
可變部分根據固定部分中的Data Length來肯定長度;服務器
Redis的客戶端與服務端採用叫作 RESP(Redis Serialization Protocol)的網絡通訊協議交換數據,相對來講仍是比較簡單的,如下是這個協議的通常形式:
*< 參數數量 > CR LF $< 參數 1 的字節數量 > CR LF < 參數 1 的數據 > CR LF ... $< 參數 N 的字節數量 > CR LF < 參數 N 的數據 > CR LF
以上大體介紹了三種比較有表明性的協議,雖說每種協議都有各自的使用場景,可是若是咱們本身去定義協議,仍是有一些相通的東西;
下面咱們重點看看去自定義協議有哪些須要咱們關注的點,如下是本人根據本身的理解整理了以下關注點:
下面分別逐一詳細介紹:
咱們平時常常講數據包,可是TCP其實只有流的概念,並無數據包的概念;那很重要的一點就是咱們的程序怎麼知道如今的業務數據已經接受所有接收完了,能夠做爲一個完整的數據包去處理了,若是不去作處理的話就會出現咱們常說的半包和粘包問題;主流的的處理方式大體有這麼兩種:
可能不一樣的協議有不一樣的叫法,我這裏把它叫作協議號,我的理解就是根據這個協議號,服務器端知道去執行什麼邏輯;好比http協議請求行中的/test.html,dubbo協議中的服務名+版本號,redis中的具體要執行什麼key;
這個是否須要仍是要看各自的場景,好比redis協議足夠簡單,無需任何標識,全部的東西都是雙端約定好的;可是其餘不少協議仍是有一些須要的,除了上面說到的能夠在消息頭中指定dataLength,其實還有不少其餘的東西能夠指定好比:
業務數據每每在整個數據包中是最大的,同時也是大小可變的部分;咱們上面所作的這些其實都是在爲業務數據服務,業務數據須要在網絡傳輸,最重要的一點就是序列化,通常就如下兩種方式:
是否須要預留字段這個得看狀況,好比http協議整個消息頭是可變的,每一行一個標識,知道讀取到空行,表示消息頭結束下面就是正文了,能夠理解爲http使用了兩種方式來保證完整包,消息頭使用特殊字符結尾,正文使用在消息頭中指定dataLength;這種方式其實它的整個擴展性是很是好的;
另一種像dubbo這樣,其實它的頭部至關於已經固定好了16個字節,這種狀況下是否能夠預留幾個字節防止後面的變動;
自定義協議其實在咱們真正的工做中仍是不多能接觸到的,更多的其實仍是去實現業務,可是咱們系統無時無刻不在和各類應用層協議打交道,若是咱們瞭解了各類協議,在系統出現問題時能夠作抓包分析;另外像咱們經常使用的數據庫中間件、緩存中間件等,都須要對協議都充分的瞭解,而後去實現代理。
能夠關注微信公衆號「 回滾吧代碼」,第一時間閱讀,文章持續更新;專一Java源碼、架構、算法和麪試。