Libp2p的Swarm模組負責將多個傳輸層組合到一個介面中,從而允許應用程式撥號節點,而不必指定要使用的傳輸層。它還負責協議協商、多路流複用、建立安全通訊等介面升級操作。
Network介面是Libp2p對外提供服務的介面,Swarm是Network介面的具體實現。Libp2p在Swarm中維護現有連線的狀態,維護支援的傳輸層協議。Swarm透過以下操作支援多傳輸協議:
· 為了支援多種傳輸層協議,新建節點時(NewNode)會將節點支援的傳輸層協議透過AddTransport加入Swarm的transports結構中。
· 當節點開啟監聽(Listen)時,Swarm模組會呼叫TransportForListening獲取監聽地址對應的傳輸層協議,並呼叫相應的傳輸層協議的Listen函式。
· 當節點主動連線(Dial)其它節點時,Swarm模組會呼叫TransportForDialing獲取撥號地址使用的傳輸協議,並呼叫相應的傳輸層協議的Dial函式。
Libp2p透過“multiaddr”的編碼方案來統一不同協議的地址格式,在Swarm模組根據“multiaddr”解析協議並呼叫相應協議的介面完成具體操作,從而達到了應用層不需要關注使用的傳輸層協議的目的。
資料安全傳輸
以上過程在節點間建立了連線,Libp2p是如何保證傳輸資料隱私的?這裡以TCP協議為例進行展開介紹。
TcpTransport是TCP的傳輸層實現模組,其中組合了Upgrader模組,Upgrader負責把一個原始的TCP連線升級為支援加密,支援多路流複用的連線。secio和tls是兩個實現了SecureTransport介面的庫,Libp2p庫預設使用secio加密庫。
以上為TCP收到一個遠端節點連線請求的呼叫流程,主動撥號遠端節點呼叫過程類似。這裡使用secio包的runHandshakeSync函式對連線進行加密。
secio庫金鑰交換使用Diffie-Hellman金鑰協商演算法(secio協議具體內容)。當然也可以使用tls對連線進行加密。
多路流複用
Libp2p應用程式通常會在節點之間開啟許多獨立的通訊流,並且可能會與某個遠端節點同時開啟多個併發流。多路流複用允許與遠端節點建立一次連線即可完成整個生命週期的資料收發,同樣只需要處理一次NAT操作,因為和同一個遠端節點所有的流都共享相同的底層傳輸連線。在配置Libp2p時,會啟用流複用模組,Swarm將在撥號遠端節點和偵聽連線時使用它們。如果遠端節點支援相同的多路流複用實現,則Swarm將在建立連線時選擇並使用它;如果撥號Swarm已經與之建立連線的遠端節點,則新建流將自動在現有連線上進行多路複用。
upgrader的muxer模組負責將加密後的連線升級為支援多路流複用的連線。MuxedConn為多路流複用操作介面,multiplex為多路流複用操作具體實現,負責具體的流建立及管理。除了預設的multiplex,Libp2p還支援yamux、spdy、muxado等不同的多路複用器實現。
還是以監聽為例,收到連線請求後,Upgrader的Secure模組首先會將連線升級為加密連線。然後透過NewMultiplex建立多路複用器例項,多路複用器例項把介面升級為支援流複用的介面。
MuxedConn的OpenStream介面用於向遠端節點傳送新建流請求,AcceptStream介面用於接收遠端節點建立流的請求。
由於節點間通訊多個流使用的仍是同一個連線,為了區別不同的流,multiplex模組實際上是對傳送的資料新增了header欄位。
header高61bits為stream id,每次新建流時stream id會自增。
header低3位表示訊息型別,總共定義了4種流訊息:
newStreamTag = 0 // 建立流訊息
messageTag= 2 // 資料訊息
closeTag= 4 // 關閉流訊息
resetTag= 6 // 重置流訊息
Libp2p透過upgrader的muxer模組將普通介面升級為支援流複用的介面,節點間資料傳輸時會在連線上透過流進行並行資料收發,而不是新建連線,從而減少了節點間新建連線的消耗。為了區別不同的流上的資訊,流複用器對收發資料新增了表示流資訊的header欄位。
總結
從Libp2p解決資料傳輸問題能夠看到Libp2p有很多小元件組成,解決相同問題小元件遵循相同介面,可以根據使用場景進行替換,各元件庫可以單獨開發升級,而不會對其它部分產生影響。“multiaddr”的地址編碼方案使基於Libp2p的應用層開發不需要關注底層使用的傳輸層協議。節點間可以透過協議協商選擇共同支援的加密模組對資料傳輸通道進行加密,從而保證資料隱私性。