應用層協議設計的反面教材

1 糟糕設計之一:消息格式「包頭+數據+包尾」安全

  與UDP不一樣,TCP通訊屬於流式通訊,沒有消息邊界,因此須要應用層自行對報文進行界定分離。實際項目1中,包頭爲{{兩個字節,包尾爲}}兩個字節,例如{{t=123}}。其格式爲:網絡

  開始邊界+消息1+結束邊界+開始邊界+消息2+結束邊界+開始邊界+消息3+結束邊界+....佈局

  因爲TCP是安全的傳輸層協議,除非特別須要,應用層無需再作校驗。消息邊界只須要一個標識便可,基本格式爲:編碼

  消息1+邊界+消息2+邊界+消息3+邊界+...spa

  不管從節約網絡帶寬,仍是從簡化報文解析代碼,第一種設計都是很是的愚蠢!操作系統

  無獨有偶,項目2中基於串口的通訊應用層協議也採用了這種設計格式。設計

  當問其設計人員爲什麼如此設計時,說一直就是這麼設計的,本身也不知道這麼設計的緣由,還美滋滋地說一直沒有什麼問題,真想揍他一拳。開發

  2 糟糕設計之二:用結構體代碼而不是文本描述消息結構字符串

  項目2中,根本無協議的描述文本,只有一個包含結構體定義的頭文件供協議的使用者參考。編譯器

  通訊就會涉及到多個機器,因此通訊協議必需要能跨平臺。而咱們知道

  struct A

  {

  char x;

  int y;

  };

  在不一樣編譯器,不一樣平臺,不一樣編譯選項下會有不一樣的二進制佈局。何況協議使用者也可能看不懂C系語言代碼。更搞笑的是,頭文件中居然沒有強制結構體單字節對齊。

  問到協議的設計者設計思路時,說咱們公司一直這樣啊,一直沒問題啊。之因此沒有問題,是由於使用這個協議的全部機器都是同一CPU型號,同一開發環境,同一操做系統。

  3 糟糕設計之三:傳送二進制浮點數

  浮點數的二進制格式並非只有一種,不一樣平臺採用不一樣的方式存放。這要比大端小端的整數差異更加嚴重。因此跨平臺傳送二進制浮點數是很是不安全的。而在項目2中,消息中大量使用了二進制浮點數。

  要傳送浮點數,一般有兩種解決方式:

  文本化。也就是傳送描述浮點數的字符串,咱們知道字符串是徹底跨平臺的,尤爲是在UTF-8這樣全球統一字符編碼的狀況下。

  轉換爲整數。例如1.2,能夠用整數12代替,只是要規定單位爲0.1便可。

  4 糟糕設計之三:大量備用字段

  項目二的消息結構體相似以下:

  struct A

  {

  char name[16];

  int age;

  int spare1;

  short spare2;

  short spare3;

  int spare4;

  };

  大量的備用字段充斥在結構體中。少許的備用字段能夠理解,如此大量的後備力量,真是深遠謀慮啊。真不知道協議使用者在看到spare時會不會吐。若是真的須要這麼多備用字段,徹底能夠從新定義一個消息結構了。

  5 糟糕設計之四:照貓畫虎的握手和校驗

  握手和校驗是保證安全完整通訊的基本手段,可是其實現卻很是不簡單,看看TCP的實現代碼就知道了,須要考慮各類異常狀況。項目二中串口設備和主機之間照貓畫虎地定義了一個握手協議。開機後 設備向主機一直髮送AA,主機收到AA後向設備發送AA,設備收到AA後向主機發送55,主機收到55後向設備發送55。這個簡單的握手存在不少問題,隨便說幾個:

  徹底沒有必要握手。通常的串口設備無需知道主機的工做狀態,主機若是想了解設備狀態,發個詢問報文便可。

  若是主機發送AA後程序退出,那麼串口設備永遠也等不到來自主機的55。

  若是主機中途關掉,在運行時可能收到來自串口設備的AA,而此時的AA其實只是消息報文的一個字節,而不是握手信號。

  只要仔細想一想,還有不少相似的狀況須要處理。並且實際使用過程當中,確實發生了上面的狀況,導致必須重啓串口設備或主機。

  仍是項目2中,基於UDP的應用層協議自行設計了校驗。其實這也無可厚非,好比著名的tftp就是這樣的協議。只是設計者考慮不周,各類問題頻出,最終的結果是這些校驗字段根本就沒有實際使用,白白浪費了網絡帶寬。須要說明的是,這個協議的設計者仍是國內很大的一家公司,固然是國企,你懂的。

相關文章
相關標籤/搜索