Java網絡教程:Protocol Design
如果設計一個客戶端到服務器的係統,那麼同時也需要設計客戶端和服務器之間的通信協議。當然,有時候協議已經為你決定好了,比如HTTP、XML_RPC(http response 的 body 使用xml)、或者SOAP(也是http response 的 body 使用xml)。設計客戶端到服務端協議的時候,一旦協議決定開啟一會兒,來看一些你必須考慮的地方:
1. 客戶端到服務端的往返通訊
2.區分請求結束和響應結束。
3.防火牆穿透
客戶端-服務端往返
當客戶端和服務端通信,執行操作時,他們在交換信息。比如,客戶端執行一個服務請求,服務端嚐試完成這個請求,發回響應告訴客戶端結果。這種客戶端和服務端的信息交換就叫做往返。示意圖如下:
當一個計算機(客戶端或者服務端)在網絡中發送數據到另一個計算機時,從數據發送到另一端接收數據完會花費一定時間。這就是數據在網絡間的傳送的時間花費。這個時間叫做延遲。
協議中含有越多的往返,協議變得越慢,延遲特別高。HTTP協議隻包含一個單獨的響應來執行服務。換句話說就是一個單獨的往返。另一方麵,在一封郵件發送前,SMTP協議包含了幾個客戶端和服務端的往返。
在協議中有多個往返的原因是:有大量的數據從客戶端發送到服務端。這種情況下你有2個選擇:
1.在分開往返中發送頭信息;
2.將消息分成更小的數據塊。
如果服務端能完成頭信息的一些初始驗證 ,那麼分開發送頭信息是很明智的。如果頭信息是空白的,發送大量數據本身就是浪費資源。
在傳輸大量數據時,如果網絡連接失敗了,得從頭開始重新發送數據。數據分割發送時,隻需要在網絡連接失敗處重新發送數據塊。已經發送成功的數據塊不需要重新發送。
區分請求結束和響應結束
如果協議容許在同一個連接中發送多個請求,需要一個讓服務端知道當前請求何時結束、下一個請求何時開始。客戶端也需要知道一個響應何時結束了,下一個響應何時開始。
對於請求有2個方法區分結束:
1.在請求的開始處發送請求的字長
2.在請求數據的最後發送一個結束標記。
HTTP用第一個機製。在請求頭中 發送了“Content-Length”。請求頭會告訴服務端在頭文件後有多少字節是屬於請求的。
這個模型的優勢在於沒有請求結束標誌的開銷。為了避免數據看上去像請求結束標誌,也不需要對數據體進行編碼。
第一個方法的劣勢:在數據傳輸前,發送者必須知道多少字節數將被傳輸。如果數據時動態生成的,在發送前,首先你得緩存所有的數據,這樣才能計算出數據的字節數。
運用請求結束標誌時,不需要知道發送了多少字節數。隻需要知道請求結束標誌在數據的末尾。當然,必須確認已發送的數據中不包含會導致請求結束標誌錯誤的數據。可以這樣做:
可以說請求結束標誌是字節值255。當然數據可能包含值255。因此,對數據中包含值255的每一個字節添加一個額外的字節,還有值255。結束請求標誌被從字節值255到255之後的值為0。如下編碼:
255 in data –>255, 255
end-of-request –> 255, 0
這種255,0的序列永遠不會出現在數據中,因為你把所有的值255變成了255,255。同時,255,255,0也不會被錯認為255,0。255,255被理解成在一起的,0是單獨的。
防火牆穿透
比起HTTP協議,大多數防火牆會攔截所有的其他通信。因此把協議放在HTTP的上層是個好方法,像XML-RPC,SOAP和REST也可以這樣做。
協議置於HTTP的上層,在客戶端和服務端的HTTP請求和響應中可以來回發送數據。記住,HTTP請求和響應不止包含text或者HTML。也可以在裏麵發送二進製數據。
將請求放置在HTTP協議上,唯一有點奇怪的是:HTTP請求必須包含一個“主機”頭字段。如果你在HTTP協議上設計P2P協議,同樣的人最可能不會運行多個“主機”。在這種情況下需要頭字段是不必要的開銷(但是個小開銷)。
最後更新:2017-05-19 17:02:17