如何自定義協議

前言

何爲自定義協議,實際上是相對標準協議來講的,這裏主要針對的是應用層協議;常見的標準的應用層協議如http、ftp、smtp等,若是咱們在網絡通訊的過程當中不去使用這些標準協議,那就須要自定義協議,好比咱們經常使用的RPC框架(dubbo,thrift),分佈式緩存(redis,memcached)等都是自定義協議;本文就來說講如何去自定義私有協議,在此以前咱們先考慮一下爲何要自定義協議。html

爲何要自定義協議

直接使用標準的協議好處是顯而易見的,我我的理解的幾點優勢:面試

  • 既然是標準協議說明已經成爲了標準,這樣不少系統就能夠直接對接,無縫集成;
  • 協議最重要的一點就是編碼解碼,標準協議每每有現成的編碼解碼包,直接拿來使用,減小開發時間;
  • 有不少圍繞標準協議的第三方測試工具,能夠很方便的進行測試;

既然有這麼多優勢那咱們爲何還要去自定義協議,大體出於如下幾點考慮:redis

  • 既然是標準協議,每每兼顧的東西比較多,致使協議數據相對來講比較大,這樣可能在一些追求性能,流量的系統中不能容忍;
  • 標準協議有不少,沒有哪種協議能夠適用任何場景中,因此若是在某個場景中尚未既定的標準協議,這時候會有各類私有協議;
  • 自定義協議只要雙方約定好數據結構就行,不具備通用性,理論上來講會更加安全一點,固然如今不少標準協議都有安全版本,好比https,sftp等等;

以上只是我的的一點理解,歡迎你們補充;關於如何去自定義協議,其實能夠去多參考一些主流的標準協議或者私有協議,其實有不少共同點能夠去借鑑;下面先簡單看看那些主流的協議;算法

主流協議

下面分別看看一些主流的標準協議或者私有協議都是如何去定義本身的數據結構的,對咱們有很是好的借鑑意義;數據庫

http協議

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協議

dubbo協議格式能夠直接參考官網提供的以下圖片:
image
看上圖其實整個協議數據包也大體分爲兩個部分:固定部分和可變部分,或者叫消息頭和消息體;
固定部分一共是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協議

Redis的客戶端與服務端採用叫作 RESP(Redis Serialization Protocol)的網絡通訊協議交換數據,相對來講仍是比較簡單的,如下是這個協議的通常形式:

*< 參數數量 > CR LF
$< 參數 1 的字節數量 > CR LF
< 參數 1 的數據 > CR LF
...
$< 參數 N 的字節數量 > CR LF
< 參數 N 的數據 > CR LF

以上大體介紹了三種比較有表明性的協議,雖說每種協議都有各自的使用場景,可是若是咱們本身去定義協議,仍是有一些相通的東西;

如何自定義協議

下面咱們重點看看去自定義協議有哪些須要咱們關注的點,如下是本人根據本身的理解整理了以下關注點:

  • 完整的數據包
  • 協議號
  • 消息頭標識
  • 業務數據
  • 預留字段

下面分別逐一詳細介紹:

完整的數據包

咱們平時常常講數據包,可是TCP其實只有流的概念,並無數據包的概念;那很重要的一點就是咱們的程序怎麼知道如今的業務數據已經接受所有接收完了,能夠做爲一個完整的數據包去處理了,若是不去作處理的話就會出現咱們常說的半包和粘包問題;主流的的處理方式大體有這麼兩種:

  • 在消息頭部加上數據包長度描述,好比在http協議和dubbo協議中出現的dataLength字段;
  • 用特殊的字符串做爲數據包的結尾,這樣咱們在接受數據的時候接受到預約的特殊字符串就表示數據包完整了;

協議號

可能不一樣的協議有不一樣的叫法,我這裏把它叫作協議號,我的理解就是根據這個協議號,服務器端知道去執行什麼邏輯;好比http協議請求行中的/test.html,dubbo協議中的服務名+版本號,redis中的具體要執行什麼key;

消息頭標識

這個是否須要仍是要看各自的場景,好比redis協議足夠簡單,無需任何標識,全部的東西都是雙端約定好的;可是其餘不少協議仍是有一些須要的,除了上面說到的能夠在消息頭中指定dataLength,其實還有不少其餘的東西能夠指定好比:

  • 業務數據格式:文本格式,json格式,html格式等等;
  • 壓縮格式:可能爲了追求流量包大小對數據包進行壓縮,gzip、deflater、snappy等;
  • 加密算法:可能須要對個人業務數據進行加密處理,保證業務數據的安全性AES、DES等;

業務數據

業務數據每每在整個數據包中是最大的,同時也是大小可變的部分;咱們上面所作的這些其實都是在爲業務數據服務,業務數據須要在網絡傳輸,最重要的一點就是序列化,通常就如下兩種方式:

  • 文本方式:序列化文本文檔text,或者json串,xml格式等;
  • 二進制方式:常見的好比protobuf,thrift,kyro等;

預留字段

是否須要預留字段這個得看狀況,好比http協議整個消息頭是可變的,每一行一個標識,知道讀取到空行,表示消息頭結束下面就是正文了,能夠理解爲http使用了兩種方式來保證完整包,消息頭使用特殊字符結尾,正文使用在消息頭中指定dataLength;這種方式其實它的整個擴展性是很是好的;
另一種像dubbo這樣,其實它的頭部至關於已經固定好了16個字節,這種狀況下是否能夠預留幾個字節防止後面的變動;

總結

自定義協議其實在咱們真正的工做中仍是不多能接觸到的,更多的其實仍是去實現業務,可是咱們系統無時無刻不在和各類應用層協議打交道,若是咱們瞭解了各類協議,在系統出現問題時能夠作抓包分析;另外像咱們經常使用的數據庫中間件、緩存中間件等,都須要對協議都充分的瞭解,而後去實現代理。

感謝關注

能夠關注微信公衆號「 回滾吧代碼」,第一時間閱讀,文章持續更新;專一Java源碼、架構、算法和麪試。
相關文章
相關標籤/搜索