系統設計,協議先行。
大部分人不了解協議的設計細節,更多使用已有協議進行應用層設計,例如:
(資料圖)
無論如何,了解協議設計的原則,對深入理解系統通信非常有幫助。
一、協議的分層設計所謂“協議”,是雙方共同遵守的規則,例如:離婚協議,停戰協議。協議有語法、語義、時序三要素:
語法,即數據與控制信息的結構或格式;語義,即需要發出何種控制信息,完成何種動作以及做出何種響應;時序,即事件實現順序的詳細說明;畫外音:后文主要講語法設計。
協議設計通常分為三層:應用層協議、安全層協議、傳輸層協議。
下面分別看下這三層的協議應該如何選型。
二、應用層協議設計應用層協議選型,常見的有三種:文本協議、二進制協議、流式XML協議。
(1) 文本協議
文本協議是指“貼近人類書面語言表達”的通訊傳輸協議,典型的協議是HTTP協議,一個HTTP協議的請求報文樣例如下:
GET / HTTP/1.1User-Agent: curlHost: musicml.netAccept: */*
文本協議的特點是:
可讀性好,便于調試;擴展性較好,能通過key:value擴展;解析效率不高,一行一行讀入,按照冒號分割,解析key和value;對二進制不友好,比如語音/視頻等;(2) 二進制協議
二進制協議即binary協議,典型是IP協議,以下是IP協議的一個圖示:
二進制協議一般包含:一般包含:
定長包頭;可擴展變長包體;一般每個字段有固定的含義,以IP協議為例,前4個bit表示協議版本號(Version);二進制協議的特點是:
可讀性差,難于調試;畫外音:打日志一般需要一個toString()函數增強可讀性。擴展性不好,如果要擴展字段,舊版協議就不兼容了,所以設計時一般會有一個Version字段;解析效率超高,幾乎沒有解析代價,二進制流的每個字段表示固定含義;天然支持二進制流 ,比如語音/視頻;這是一個典型的16字節二進制定長包頭的例子:
//sizeof(cs_header)=16struct cs_header { uint32_t version; uint32_t magic_num; uint32_t cmd; uint32_t len; uint8_t data[];}__attribute__((packed));
其中:
(1)前4個字節表示版本號version;
(2)接下來4個字節表示魔法數字magic_num,用來解決數據錯位或丟包問題;
畫外音:例如,約定好魔法數字是0x01020304,收到的報文,魔法數字匹配,認為是正常報文,否則認為是報文異常,斷開連接。
(3)接下來4個字節表示命令號command,不同的命令號對應不同的變長包體;
(4)最后4個字節表示包體長度length,以確定變長包體有多少字節;
這是一個實際的二進制變長包體:
message CUserLoginReq { optional string username = 1; optional string passwd = 2;}message CUserLoginResp { optional uint64 uid =1;}
它使用的是Google的Protobuf協議,容易看到:
請求報文傳入的是用戶名與密碼;響應包返回的是用戶的uid;PB是很流行的二進制變長包體協議,其優點為:
通用,可以生成C++、Java、PHP等多語言代碼;自帶壓縮功能;對二進制友好;在工業界已廣泛應用;畫外音:Google出品,必屬精品。流式XML協議流式XML似乎是文本協議的一個特例,亦可以單獨作為一類。例如:xmpp就是典型的流式XML協議,下面是xmpp協議的一個典型報文:
從xml標簽中大致可以判斷這是一個romeo發給juliet的聊天消息。 XML協議有幾個特點: 安全層協議設計,除了使用SSL,自行實現的話,常見的又有以下三種方案。 畫外音:SSL秘鑰管理是個問題。 (1) 固定密鑰 服務端和客戶端約定好一個密鑰,同時約定好一個加密算法(例如:AES),每次客戶端發送報文前,就用約定好的算法,以及約定好的密鑰加密再傳輸,服務端收到報文后,用約定好的算法,約定好的密鑰再解密。 畫外音:安全性低,安全性基于程序員的職業操守。 (2) 一人一密 簡單來說,就是一個人的密鑰是固定的,但是每個人之間又不同。常見的實現方式是: (3) 一次一密 即動態密鑰,一Session一密鑰的安全性更高,每次會話前協商密鑰。密鑰協商的過程要經過2次非對稱密鑰的隨機生成,1次對稱加密密鑰的隨機生成,具體詳情這里不展開。 可選的協議有TCP和UDP,現在基本都是使用TCP,有了epoll等技術后,多連接就不是瓶頸了,單機幾十萬鏈接沒什么問題。
X 關閉
X 關閉