layout: post title: 2016 08 03 信息系統實踐手記8 兩模塊通訊的一些事 key: 20160803 tags: 對接 卡口 黑名單 佈防 撤防 訂閱 取消 設備 列表 模型 modify_date: 2016 08 03 信息系統實踐手記8 兩模塊通訊的一些事 說明: ...
layout: post
title: 2016-08-03-信息系統實踐手記8-兩模塊通訊的一些事
key: 20160803
tags: 對接 卡口 黑名單 佈防 撤防 訂閱 取消 設備 列表 模型
modify_date: 2016-08-03
---
信息系統實踐手記8-兩模塊通訊的一些事
說明:
正文:
- 信息系統實踐手記系列是系筆者在平時研發中先後遇到的大小的問題,其中比較典型的內容加以收集和分享。
- 信息系統實踐手記目錄:博客園(或查看源碼的README.MD文件)
摘要:
- 此文描述了兩個模塊通信方面的內容。
正文
在信息系統開發和對接的過程中,免不了要遇到兩個模塊之間的消息互通,即便都是自己開發的2個模塊互通,或者自己模塊和第三方某個模塊通訊。
這裡儘量不涉及具體技術(這個大家自己找資料看書),主要是掰扯一下會涉及到的一些思考和情況。
1. 對接協議的2個層次:
- 兩個代碼實體(簡稱模塊)互相需要通訊,往往在不同的主機(PC/IP)上,也偶有在同一個主機上的,TCP/IP協議已經屏蔽了物理機的差異,通過IP/PORT來區分資源,區分模塊或功能對等實體。回過來,2個模塊要通訊,2端代碼要交互信息(數據/消息/信令/碼流/結構/隨便怎麼稱呼),參照目前互聯網的TCP/IP協議或其他奇怪協議,基本分為兩種層次:
- A:物理實現層(對於TCP/IP中的物理層,鏈路層等)的比特流,二進位,高低電平,網卡,網路設備等支持這個協議。比如3伏特/12伏特表示0或1等等,這是電氣特性;可靠性依賴於硬體的工作特性,電氣特性,以及糾錯手段(一些數學和工程方法)。
- B:高層(統一叫高層,就是這個意思),基於二進位bit層次上的有含義的層次,位元組流層次,byte(一般是8bit=1byte),而byte就對應了byte編碼規範,比如ASCII或UTF-8,這樣一串bytes就有邏輯含義了(26個英文字母及大小寫,自然數10個數字,特殊符號,回車換行,加減乘除符號,不可見符號等等);其實網路上傳遞的都是位元組流,而TCP/IP協議主要也是處理這個位元組流從端到端的傳送;可靠性依賴於下層A的bit流,並也可以增加適當的校驗CRC等各種手段(也是一些數學方法)
- 所以雖然ISO/OSI的7層網路模型設計的很好,其實落實到實際實現的TCP/IP協議,也就主要分為以上A/B兩個層次,A中還細分幾個,B中也有細分但用的不多;主要理解這2個層次,從物理bit的可靠傳輸,架構其有邏輯含義的高層位元組byte傳輸,這麼個概念。後續主要就談B層了,畢竟A層次目前已經穩定,除非量子力學顛覆當前的電腦原型(節點PC),然後自然而然的可能顛覆網路基礎物理層A的電氣特性,搞些先進的,這個我們就不談了,還有5到10年就撲面而來了。
這裡只談B層;
2. TCP/IP協議主要分為TCP/UDP2種:
- TCP/IP協議分為主要的2種,UDP/TCP:
- TCP:面向連接(在網路基礎層A上維護的邏輯上的連接通道) 的有狀態的位元組流通訊,保證數據包的按序及正確傳送,有狀態,所以能錯誤重傳,並維護連接通道;但數據報的緩存及發送依賴於“邏輯連接通道”兩端模塊程式(往往是OS的網路驅動,網卡的驅動程式等)自主決定。所以會產生粘包及粘包如何分割的問題,這是TCP經典問題,請另查資料(或看netty官網的userguide入門章節,有比較簡單經典的描述);
- 場景:需要可靠連接,直接拿來用的場景,犧牲一定的效率;用的較廣泛;
- UDP:無連接,不保證數據包按照發送順序到達目的地,甚至不保證能到達目的地;它其實允許2端模塊自己通過高層協議來組織,數據包的順序檢查,丟包重傳,及狀態流轉等;等於自己實現一個小小的簡單TCP;
- 場景: 網路環境表穩定可靠,對數據正確性及順序無特別嚴格要求,效率較高;比如區域網播放視頻等;
- 說明:隨著網路硬體性能,模塊所在節點PC或伺服器的性能提升,也許大家覺得即使看視頻也用TCP來的更方便和效果好,那也是可以的,這無一個定論,看具體情況和項目方案的需求和實施情況而定
4. 針對TCP的幾種使用方法
- TCP比較好用,但是有個特點就是粘包。雖然它是面向連接,有狀態維護管理,丟包重傳,且保證次序。但是兩端節點上的莊模塊的網路驅動底層庫,各自有演算法,緩存cache和策略,效率,切分包的大小(MTU)等都不同,導致接收方得到的包只在整體位元組流bytes上是一致的,但無法知道發送方每次send給底層的byte位元組是如何劃分的。這就是經典的粘包及切分問題;一般有如下幾個方法來處理;
- 方法1:定長欄位法;
- 這樣就不怕粘包了,發送端只管發,接收端從接受緩存不斷輪詢,如果滿了一定長度length後,就讀入;這裡當然假設TCP是有序,丟包會重傳的。
- 優點:實現簡單;邏輯也簡單;
- 缺點:一旦TCP傳輸有錯(網路不好,概率偶發,協議棧缺陷等,總會錯誤),則整體就會偏移,數據錯位就會錯誤;
- 改進:根據其缺點,可在定長中增加特殊頭標記,比如定長length=64bytes,其中開始標記flag=\x0C等,當滿足兩者,就解碼為邏輯內容,內容正確就用,內容錯誤就丟棄,並找到下一個有效的定長數據片(且以flag開頭);這其實已經有一點TLV的雛形了。
- 方法2:分隔符方式(變長欄位法);
- 和定長欄位法相對的,就是通過一個分隔符來區分前後兩端內容,則沒斷長度可變化,分隔符不可重覆,同樣能解決粘包問題;
- 優點:實現簡單,邏輯較簡單,比較可靠;
- 缺點:要保證分隔符是payload(有效載荷,即實際負載和傳輸的數據)中不能包含的!而且演算法效率低下,需要每個byte都檢查是否為分隔符;
- 改進:如果能配合定長的數據片,那其實等價於加一個flag頭。
- 方法3:TLV方式(經典高效):
- TLV是比較經典的方式,用的相對較多。TLV(一般指Type-Length-Value)。它的協議一般會約定一個head頭結構,包含flag和length,flag用來找到TLV的頭(是個標記位元組,不必唯一,但不能很多),找到flag後,就根據協議約定解析head頭結構,解析失敗則繼續查找flag;解析成功後,就能獲得head+body的整體長度length,從而解析得到body的數據;在body中通過一組組不定長的(length-value)結構,一個長度一個value值來承載payload,但length欄位的自身長度是固定的,一般為1都4個位元組之間,看TLV整體的協議定義;body的length和head的中length的自身長度也不必要一定一致。另外,這個type是類型的含義,是TLV協議手冊的約定,一般會說明有K組
- 優點:高效的通過length欄位獲取value,不用每個位元組都檢查;且支持不同的數據結構;
- 缺點:實現比較複雜,兩邊TLV協議手冊顯然比前2種方法的描述要詳細;
- 改進:可以通過諸如JAVA的反射或其他方法,來將TLV協議通過“配置文件”的方式,讓程式自動根據清晰明瞭容易維護的配置文件來生成編解碼程式(CODEC)。
4. 針對TCP心跳的一些說明
TCP協議內容較多,有好幾本大書,但核心內容,網上也轉載很多,這裡只提一些內容。
TCP如果不在初始化socket的時候給予超時timeout設定(看tcp類庫支持哪些超時,一般如java io/NIO支持連接超時,傳輸超時等),那麼預設就是2小時(120分鐘),在120分鐘內TCP協議內部自己有心跳維持socket連接通道,
超過120分鐘,則按照狀態機變化,兩端都開始拆除TCP連接。當然,兩端莊模塊開發的如果不好,這就會導致對端有大量埠TIME_WAIT等等TCP狀態機不能順利,快速,高效的拆除並被覆用,導致資源消耗,甚至耗光65535個port口。
除了TCP自身的超時,或者網路lib庫能力範圍內給予的連接超時,傳輸超時(閑置idle超時)外,樁模塊可以人為的在邏輯層添加自己的超時和心跳機制。舉例如下:
(1)收到PING,就發PONG,收到PONG,不響應;這是對等心跳探測包,有發起者探測對方是否活著alive;
(2)兩邊約定心跳間隔時間,比如每120秒發送心跳包(比如“HB”2個字元,或某種位元組等),或者約定只有server發給client,不用反向;
特別說明:這是高層邏輯的心跳,應用層自己使用的,而且其實現根據前述3種使用方法而不同,比如你用TLV,那麼也需要額外定義一個“心跳包”的協議;
5. 用哪些類庫?
一般開發樁模塊的時候,兩端約定好傳輸協議,比如TLV,那麼就各自開發。你用熟悉的語言比如java,而且會進一步使用現成的框架,比如netty(非同步io框架),
這樣你就不用赤裸的使用java的nio自己一步步處理,不用重覆發明輪子。netty是一個非常好用的庫,開發者同時也開發了mina,兩個庫有點類似,各有千秋和側重。
詳見我整理的[ITIS-資料集合貼]中的介紹和電子書;
6. 對接協議的的安全性問題
TCP是有連接,有狀態,有序,丟包重傳的,所以他有基礎的正確性保證。但TCP完全會受到重傳攻擊,或偵聽竊聽,或篡改數據包等破壞方式。而且TCP保證的是網路層的位元組流的按序正確到達,不保證上層邏輯(應用層)認為的數據是百分百正確的。所以,有如下建議:
(1)可以根據需求,對自己的數據進行編碼,壓縮,加密;
(2)可以參考一些開源的密鑰,加密演算法,認證,證書等協議的使用;
(3)曾經這樣弄過,給payload轉為base64,並且做了MD5摘要,傳到對端MD5用來驗證數據對不對,也可作為非同步反饋的key值。
總結,總是TCP協議豐富多彩,如果熟悉一些框架和常用手段,再加上一些諸如MD5,加密壓縮演算法等,完全可以自己搭建和實現不同的傳輸協議,滿足不同情況的要求,在兩個對端樁模塊之間實現數據的傳遞。