P2P技術詳解(三):P2P中的NAT穿越(打洞)方案詳解(進階分析篇)

1、NAT和NAPT

網絡地址轉換(NAT,全稱Network Address Translation),早期的NAT指的是Basic NAT(靜態NAT),它在技術上比較簡單一點,僅支持地址轉換,不支持端口映射。這就需要對每一個當前連接都要對應一個IP地址,因此要維護一個公網的地址池。

我們可以看出,Basic NAT一個比較明顯的缺陷就是:同一時刻只能少量位於NAT後面的機器能夠和外部交互(要看NAT有幾個外網IP)。

後期的NAT基本都指的是NAPT(網絡地址端口轉換)了,這種方式支持端口的映射並允許多臺主機共享一個公用IP地址,這樣就可以支持同時多個位於NAT後面的機器和外部進行交互了。

支持端口轉換的NAT又可以分爲兩類:

1)源地址轉換(SNAT);
2)目的地址轉換NAT(DNAT)。

下面說的NAT都是指NAPT。

2、NAT帶來的問題

NAT在緩解IPv4地址資源的緊張的同時,也帶來了不少問題:

1)NAT使IP會話的保持時效變短;
2)NAT在實現上將多個內部主機發出的連接複用到一個IP上,這就使依賴IP進行主機跟蹤的機制都失效了;
3)NAT工作機制依賴於修改IP包頭的信息,這會妨礙一些安全協議的工作;
4)NAT限制了使用一些高層協議(FTP、Quake、SIP)的Peer兩端的P2P通信。

對於問題1:
其主要原因是,NAT設備建立的內網IP、端口到外網IP、端口的映射的表項是有一個保活期的。如果在一個超時時間內,該映射上沒有實際數據的傳輸,那麼NAT會過期並回收這個映射表項給其他通信鏈路用(IP和端口資源有限,通信的鏈路是無限)。爲了避免這種通信鏈路提前被NAT中斷的情況,很多應用層協議在設計的時候就考慮了一個連接保活的機制,即在一段時間沒有數據需要發送時,主動發送一個NAT能感知到而又沒有實際數據的保活消息,這麼做的主要目的就是重置NAT的會話定時器。

對於問題2
其主要原因是,對於NAT後面的N多主機,在外部看來都是同一個主機(NAT設備),於是來之同一個IP的數據包一定是來之同一個主機的前提判斷就會不準確了,這樣一下基於這個前提的機制(例如:TCP的TIME_WAIT的回收和重用)都會有問題。

對於問題3:
其主要原因是,NAT篡改了IP地址、傳輸層端口號和校驗和。

對於問題4:
其主要原因是,一般情況下,NAT是不允許外部的Peer節點主動連接或發送數據包給NAT後面的主機的(這裏的主動指的是,在一段時間內,首先發送數據包的一方爲主動方)。

由於NAT這種特性,那麼在NAT環境下,實現P2P通信的完整解決方案包括幾個部分呢?相關的原理、方法、技術有哪些?

對於一個完整的P2P通信解決方案,其實現包括下面兩個步驟:

1)首先在Server的協助下,通信兩端Peer嘗試相互連接,如果兩端Peer在嘗試互聯不成功後,那麼就將失敗結果反饋給Server轉入步驟2);
2)這個步驟比較簡單粗暴了,就是relay(服務器中轉),簡單的來講就是Peer1將要發給Peer2的數據發給Server,然後由Server幫忙轉發給Peer2,同樣對於Peer2來說也一樣。

對於實現P2P通信,步驟1是大家下功夫最多的,其原因比較簡單,就是步驟2需要消耗較多的服務器資源,成本比較高。步驟1實現P2P兩個節點間的直接通信,在資源消耗和效率上都是比較好的。

3、P2P通信穿越NAT的技術、方法

前常見的P2P通信穿越NAT的技術、方法主要有:

1)應用層網關;
2)中間件技術;
3)打洞技術(Hole Punching);
4)Relay(服務器中轉)技術。

4、NAT穿越技術1:應用層網關

4、1 原理

利用帶有ALG功能的NAT對特定應用層協議的支持和理解,在一個NAT網關檢測到新的連接請求時,需要判斷是否爲已知的應用類型,這通常是基於連接的傳輸層端口信息來識別的。在識別爲已知應用時,再調用相應功能對報文的深層內容進行檢查,當發現任何形式表達的IP地址和端口時,將會把這些信息同步轉換,並且爲這個新連接創建一個附加的轉換表項。

這樣,當報文到達公網側的目的主機時,應用層協議中攜帶的信息就是NAT網關提供的地址和端口。

例如:下圖,對於使用主動模式的FTP協議(PORT方式),就需要AGL的支持了。
在這裏插入圖片描述
由於FTP協議通信需要兩個TCP連接,一個是命令鏈路,用來在FTP客戶端與服務器之間傳遞命令;另一個是數據鏈路,用來上傳或下載數據。如上圖,位於NAT後面的FTP client(192.168.1.2)首先發起一個TCP連接(命令鏈路)連上外網FTP Server(8.8.8.1),然後發送PORT報文(192.168.1.2,1084)說自己在1084端口接收數據,然後進過ALG處理PORT報文變成(8.8.8.1,12487),同NAT建立其一條(192.168.1.2,1084 <—>8.8.8.1,12487)映射。這樣FTP Server發往(8.8.8.1,12487)的數據就會被轉到(192.168.1.2,1084),從而實現數據傳輸(如果沒經過ALG處理,那麼FTP Server直接連接192.168.1.2,1084是無法連接上的)。

4、2 限制

ALG技術是利用NAT本身的支持來進行NAT的穿越,這個方案有很大限制,主要的原因是ALG都是爲特定協議的特定規範版本而開發的,然而不管是協議本身,還是協議的數量都在變化,這就使得ALG適應性不強。

5、NAT穿越技術2:中間件技術

這是一種通過開發通用方法解決NAT穿越問題的努力。與前者不同之處是,AGL技術中NAT網關是這一解決方案的唯一參與者,而中間件技術中客戶端會參與網關公網映射信息的維護。UPnP就是這樣一種方法,UPnP中文全稱爲通用即插即用,是一個通用的網絡終端與網關的通信協議,具備信息發佈和管理控制的能力。

5、1 原理

NAT只要理解客戶端的請求並按照要求去分配響應的映射轉換表,不需要自己去分析客戶端的應用層數據。網關映射請求可以爲客戶動態添加映射表項。

此時,NAT不再需要理解應用層攜帶的信息,只轉換IP地址和端口信息。而客戶端通過控制消息或信令發到公網側的信息中,直接攜帶公網映射的IP地址和端口,接收端可以按照此信息建立數據連接。NAT網關在收到數據或連接請求時,按照UPnP建立的表項只轉換地址和端口信息,不關心內容,再將數據轉發到內網。

5、2 限制

這種方案需要網關、內部主機和應用程序都支持UPnP技術,且組網允許內部主機和NAT網關之間可以直接交換UPnP信令才能實施。

6、NAT穿越技術3:打洞技術(Hole Punching)

Hole Punching技術是工作在運輸層的技術,可以屏蔽上層應用層的差異,並且不需要NAT網關特定的支持,因此其通用性比較強,應用性也比較廣。

6、1 原理

打洞技術的原理比較簡單,就是NAT內網的節點需要在NAT上建立自己的一條轉發映射關係(這就是所謂的在NAT上打下一個洞),然後外網的節點就通過這個”洞”來進行通信。爲描述方便,我們將一對IP地址和端口信息的組合稱之爲一個Endpoint。

打洞原理可以簡化爲下面三個過程:

1)首先位於NAT後的Peer1節點需要向外發送數據包,以便讓NAT建立起內網Endpoint1(IP1、PORT1)和外網Endpoint2(IP2、PORT2)的映射關係;
2)然後通過某種方式將映射後的外網Endpoint2通知給對端節點Peer2;
3)最後Peer2往收到的外網Endpoint2發送數據包,然後該數據包就會被NAT轉發給內網的Peer1。

上面三個過程比較簡單,然而細心的同學會有些疑問:

1)步驟[1]中的映射關係的建立有什麼規律的麼?怎樣才能獲取到映射關係呢?
2)通知對端節點Peer2的方式一般是怎麼樣的?
3)步驟[3]一定可以實現麼?也就是Peer2往收到的外網Endpoint2發送數據包,就一定能夠被NAT轉發給內網的Peer1嗎?

對於疑問(3),如果全部會被轉發給內網Peer1,那會不會太不安全了,只要知道內網Peer1的映射後的外網Endpoint2,就可以給穿透NAT給內網Peer1發送數據,這樣內網Peer1不就很容易遭到攻擊了?如果全部都不轉發給內網Peer1,這樣Peer1只能向外發數據,而無法收到外面的數據,嚴重影響Peer1的正常通信。

那麼,這就比較明瞭了,我們需要的是一部分可以轉發,另外一部分不轉發。這就涉及到NAT對外來數據包的一個過濾規則了,而疑問(1)提到的映射關係建立的規則,這涉及到NAT的Endpoint的映射規則。
那麼問題來了,有什麼方法可以知道NAT的Endpoint映射規則和對外來數據包的過濾規則呢?

6、2 方法

由上面原理的討論我們知道,要實現打洞穿越NAT,首先需要知道NAT的行爲規則(NAT的Endpoint映射規則和對外來數據包的過濾規則),這樣才能更好地實現打洞穿越。

那NAT有哪些行爲類型?有什麼辦法來偵測NAT的行爲呢?

6、2、1 NAT行爲類型與偵測方法

NAT的行爲類型和偵測方法是由STUN(首先在RFC3489中定義,英文全稱是Simple Traversal of UDP Through NATs)協議來描述的,STUN協議包括了RFC3489、RFC5389、RFC5780、RFC5769幾個系列文檔。
早期的STUN協議是由RFC3489(經典的STUN)來描述,其定義的NAT行爲類型如下:
1)Full Cone NAT - 完全錐形NAT:
所有從同一個內網IP和端口號Endpoint1發送過來的請求都會被映射成同一個外網IP和端口號Endpoint2,並且任何一個外網主機都可以通過這個映射的Endpoint2向這臺內網主機發送包。也就是外網所有發往Endpoint2的數據包都會被NAT轉發給Endpoint1。由於對外部請求的來源無任何限制,因此這種方式雖然足夠簡單,但卻不安全。
2)Restricted Cone NAT - 限制錐形NAT:
它是Full Cone的受限版本:所有來自同一個內網Endpoint1的請求均被NAT映射成同一個外網Endpoint2,這與Full Cone相同。但不同的是,只有當內網Endpoint1曾經發送過報文給外部主機(假設其IP地址爲IP3)後,外部主機IP3發往Endpoint2的數據包纔會被NAT轉發給Endpoint1。這意味着,NAT設備只向內轉發那些來自於當前已知的外部主機的數據包,從而保障了外部請求來源的安全性
3)Port Restricted Cone NAT - 端口限制錐形NAT:
它是Restricted Cone NAT的進一步受限版,與限制錐形NAT很相似,只不過它包括端口號PORT。只有當內網Endpoint1曾經發送過報文給外部Endpoint3(包括IP和端口了),Endpoint3發往Endpoint2的數據包纔會被NAT轉發給Endpoint1。端口號PORT這一要求進一步強化了對外部報文請求來源的限制,從而較Restrictd Cone更具安全性。
4)Symmetric NAT - 對稱NAT:
上面的1)2)3)所有的Cone NAT中,映射關係只和內網的源Endpoint1相關,只要源Endpoint1不變其都會被映射成同一個Endpoint2。而對稱NAT的映射關係不只與源Endpoint1相關,還與目的Endpoint3相關。也就是源Endpoint1發往目的Endpoint30的請求被映射爲Endpoint20,而源Endpoint1發往目的Endpoint31的請求,則被映射爲Endpoint21了。此外,只有收到過內網主機發送的數據的外網主機纔可以反過來向內網主機發送數據包。

經典 STUN 定義的 NAT 行爲類型是將NAT的Mapping Behavior (映射規則)和Filtering Behavior(過濾規則)統一來歸類的,這樣對Symmetric NAT類型的歸類過於籠統,使得許多 NAT 不完全符合由它定義的類型。

於是後來,RFC3489被廢棄並由RFC5389來替代,在RFC5389中,將Mapping Behavior (映射規則)和Filtering Behavior(過濾規則)分開來,定義了3種Mapping Behavior (映射規則)和3種Filtering Behavior(過濾規則),一共有9種組合。

爲什麼是3種呢?其實理由很簡單,對於一個特定的內網源Endpoint1,影響其映射關係的因素不外乎就4種情況:

1)目的IP和目的端口PORT都無關;
2)目的IP和目的端口PORT都相關;
3)僅僅目的IP相關;
4)僅僅目的PORT相關。

對於4僅僅考慮一下PORT信息有點雞肋,基本和1差不多,於是把4去掉了。同樣,對於過濾規則也一樣。

3種Mapping Behavior (映射規則)和 Filtering Behavior(過濾規則)如下。

Mapping Behavior:
1)Endpoint-Independent Mapping:
對於一個內網的EndpointP,其映射的外網EndpointG是基本固定的,不會隨着通信外部主機的不同而變化。

2)Address and Port-Dependent Mapping:
對於一個內網的EndpointP,如果與之通信的外部爲EndpointGB1,那麼EndpointP就會被NAT映射成EndpointG1;如果與之通信的外部爲EndpointGB2,那麼EndpointP就會被NAT映射成EndpointG2。也就是隻要之通信的外部爲EndpointGB發生變化,那麼映射的外網EndpointG就會變化。

3)Address-Dependent Mapping:
對於一個內網的EndpointP,如果與之通信的外部爲EndpointGB1,那麼EndpointP就會被NAT映射成EndpointG1;如果與之通信的外部爲EndpointGB2(如果EndpointGB2的IP和EndpointGB1的相同),那麼EndpointP同樣會被NAT映射成EndpointG1,否則就會被NAT映射成EndpointG2。也就是隻要之通信的外部爲EndpointGB的IP發生變化,那麼映射的外網EndpointG就會變化。

Filtering Behavior:
1)Endpoint-Independent Filtering:
對於這種過濾類型,NAT在在自己的一個外網EndpointG1收到數據包,只要找到與之對應的內網EndpointP1,NAT就會轉發這個數據包給相應的內網EndpointP1,不管這個數據包的來源是那裏。(一般來說,這樣過濾規則的NAT是比較少的,因爲這樣的安全係數比較低)

2)Address and Port-Dependent Filtering:
對於這種過濾類型,NAT在自己的一個外網EndpointG1收到來源是EndpointGA1數據包,這個時候NAT要判斷自己是否曾經通過自己的EndpointG1給EndpointGA1發送過數據包,如果曾經發過,那麼NAT就允許該數據包通過NAT並路由給內網與之對於的內網EndpointP1;如果沒發過,那麼NAT會不允許該數據包通過NAT。

3)Address-Dependent Filtering:
對於這種過濾類型,NAT在自己的一個外網EndpointG1收到來源是EndpointGA1數據包,這個時候NAT要判斷自己是否曾經通過自己的EndpointG1給和EndpointGA1的IP相同的機器發送過數據包(這裏會忽略端口),如果曾經發過,那麼NAT就允許該數據包通過NAT並路由給內網與之對於的內網EndpointP1;如果沒發過,那麼NAT會不允許該數據包通過NAT。

RFC5389只是定義了協議的相關屬性、機制、報文結構以及一些相關的安全注意點等等,並有沒對怎麼進行完整的NAT類型偵測做介紹。而對完整NAT類型偵測過程主要由RFC5780這個文檔來描述。完整的NAT類型偵測的過程主要在RFC5780文檔的4.3和4.4節,主要分爲NAT映射規則(Determining NAT Mapping Behavior)和NAT過濾規則(Determining NAT Filtering Behavior)。

下面對具體的偵測過程做介紹:

**要進行NAT類型的偵測,需要一個具有雙公網IP的服務器來協助偵測,我們稱該服務器爲STUN Server。**假設STUN Server的雙IP分別爲IP_SA(125.227.152.3)和IP_SB(125.227.152.4) 監聽的兩個端口分別爲PORT_SA(4777)和PORT_SB(4888),客戶端A的內網地址和端口分別爲IP_CA(10.70.142.12)和PORT_CA(1234)。
1)客戶端A以IP_CA: PORT_CA給STUN Server的IP_SA: PORT_SA發送一個bind請求,STUN server以IP_SA: PORT_SA給客戶端A的IP_CA: PORT_CA回覆響應,響應內容大體爲:(NAT映射後的IP地址和端口爲:IP_MCA1: PORT_MCA1,STUN Server的另外一個IP地址和端口爲:IP_SB: PORT_SB)。這個時候客戶端判斷,如果IP_CA: PORT_CA == IP_MCA1: PORT_MCA1,那麼該客戶端是擁有公網IP的,NAT類型偵測結束。

2)客戶端A以IP_CA: PORT_CA給STUN server的IP_SB: PORT_SA(相對步驟1 ip改變了)發送一個bind請求,STUN server以IP_SB: PORT_SA給客戶端A的IP_CA: PORT_CA回覆響應,響應內容大體爲:(NAT映射後的IP地址和端口爲:IP_MCA2: PORT_MCA2)。這個時候客戶端判斷,如果IP_MCA1: PORT_MCA1 == IP_MCA2: PORT_MCA2,那麼NAT是Endpoint Independent Mapping的映射規則,也就是同樣的內網地址IP_CA: PORT_CA經過這種NAT映射後的IP_M: PORT_M是固定不變的;如果IP_MCA1: PORT_MCA1 != IP_MCA2: PORT_MCA2,那麼就要進行下面的第3步測試。

3)客戶端A以IP_CA: PORT_CA給STUN server的IP_SB: PORT_SB(相對步驟1 ip和port改變了)發送一個bind請求,STUN server以IP_SB: PORT_SB給客戶端A的IP_CA: PORT_CA回覆響應,響應內容大體爲:(NAT映射後的IP地址和端口爲:IP_MCA3: PORT_MCA3)。這個時候客戶端判斷,如果IP_MCA2: PORT_MCA2== IP_MCA3: PORT_MCA3,那麼NAT是Address Dependent Mapping的映射規則,也就是只要是目的IP是相同的,那麼同樣的內網地址IP_CA: PORT_CA經過這種NAT映射後的IP_M: PORT_M是固定不變的;如果IP_MCA2: PORT_MCA2!= IP_MCA3: PORT_MCA3,那麼NAT是Address and Port Dependent Mapping,只要目的IP和PORT中有一個不一樣,那麼同樣的內網地址IP_CA: PORT_CA經過這種NAT映射後的IP_M: PORT_M是不一樣的。

以上三個步驟是進行Mapping Behavior的偵測,下面兩個步驟是進行Filtering Behavior偵測:

4)客戶端A以IP_CA: PORT_CA給STUN server的IP_SA: PORT_SA發送一個bind請求(請求中帶CHANGE-REQUEST attribute來要求stun server改變IP和PORT來響應),STUN server以IP_SB: PORT_SB給客戶端A的IP_CA: PORT_CA回覆響應。如果客戶端A能收到STUN server的響應,那麼NAT是Endpoint-Independent Filtering的過濾規則,也就是隻要給客戶端A的IP_CA: PORT_CA映射後的IP_MCA: PORT_MCA地址發送數據都能通過NAT到達客戶端A的IP_CA: PORT_CA(這種過濾規則的NAT估計很少)。如果不能收到STUN server的響應,那麼需要進行下面的第五步測試。

5)客戶端A以IP_CA: PORT_CA給STUN server的IP_SA: PORT_SA發送一個bind請求(請求中帶CHANGE-REQUEST attribute來要求stun server改變PORT來響應),STUN server以IP_SA: PORT_SB給客戶端A的IP_CA: PORT_CA回覆響應。如果客戶端A能收到STUN server的響應,NAT是Address-Dependent Filtering的過濾規則,也就是隻要之前客戶端A以IP_CA: PORT_CA給IP爲IP_D的主機發送過數據,那麼在NAT映射的有效期內,IP爲IP_D的主機以任何端口給客戶端A的IP_CA: PORT_CA映射後的IP_MCA: PORT_MCA地址發送數據都能通過NAT到達客戶端A的IP_CA: PORT_CA;如果不能收到響應,NAT是Address and Port-Dependent Filtering的過濾規則,也即是隻有之前客戶端A以IP_CA: PORT_CA給目的主機的IP_D: PORT_D發送過數據,那麼在NAT映射的有效期內,只有以IP_D: PORT_D給客戶端A的IP_CA: PORT_CA映射後的IP_MCA: PORT_MCA地址發送數據才能通過NAT到達客戶端A的IP_CA: PORT_CA。

通過以上5個步驟就能完成完整的NAT類型偵測。

將NAT映射規則和過濾規則組合起來就形成9中不同的NAT行爲類型:

1)Endpoint Independent Mapping和Endpoint-Independent Filtering組合對應於RFC3489中的Full Cone NAT;
2)Endpoint Independent Mapping和Address-Dependent Filtering組合對應於RFC3489中的Restricted Cone NAT;
3)Endpoint Independent Mapping和Address and Port-Dependent Filtering組合對應於RFC3489中的Port Restricted Cone NAT;
4)Address and Port-Dependent Mapping和Address and Port-Dependent Filtering組合是RFC3489中所說的Symmetric NAT。

可見RFC3489只描述了9種NAT組合行爲類型中的4種。最後一個文檔rfc5769,定義了一些STUN協議的測試數據用於測試STUN server的正確性。
在這裏插入圖片描述

NAT類型判斷流程圖
在這裏插入圖片描述

6、2、2NAT打洞過程

“打洞”方式穿越NAT有兩種形式:TCP”打洞”和UDP”打洞”。原理上,TCP”打洞”與UDP”打洞”是沒有本質的區別的。

然而在實現上,TCP”打洞”的成功率遠沒UDP”打洞”的成功率高,其主要原因有三

1)有些NAT防火牆策略對TCP協議不是很友好:
有些NAT的防火牆策略不允許來路不明的外部向內網機器發起TCP連接。由於TCP是有連接的,NAT比較容易分清哪些是NAT 內網機器主動進行通信的外部節點,這樣防火牆策略比較明確。而UDP是無連接的,沒有連接來標明一個數據流,協議比較簡單,這樣NAT支持的比較多。

2)TCP協議本身:
由於TCP的TIME_WAIT狀態引起,同一個NAT後面的其他主機發起的連接被誤判。具體可以看下面的文章:km.oa.com/group/25569/articles/show/246068 。

3)TCP協議的實現API:
因爲標準的Berkeley sockets API是圍繞C/S編程而設計的。這個API通過connect()允許一個TCP流套接字初始化一個向外的連接,通過listen()和 accept()監聽一個外入的連接,一個套接字不能既用來監聽又用來初始化向外的連接。更進一步講, TCP套接字通常與本地主機上的TCP端口一一對應:一個套接字綁定到本地主機機上的某個端口後,另一個套接字就不能再綁定到該端口。然而TCP打洞要成功,需要一個本地的TCP端口既可以監聽外入的連接,同時又可以發起多個向外的連接。幸運的是,所有主流的操作系統都支持一個特殊的socket選項SO_REUSEADDR,它運行應用程序綁定多個設置了該選項的套接字到同一端口。BSD系統引入了SO_REUSEPORT選項來控制端口重用,從而把端口重用和地址重用相分離。在這樣的系統中,兩個選項都需要被設置。儘管如此,要進行TCP打洞需要進行TCP三次握手的同時打開,但是有些TCP/IP的實現,可能不支持這種同時打開的情況,這樣也就無法建立TCP連接了。

下面就幾種網絡拓撲情況下,NAT打洞步驟進行逐一介紹。爲了方便描述,假設通信的兩個節點分別爲Client A和Client B,而輔助NAT穿越的STUN Server爲Server S。下面的所有方法都要求Client A、Client B都與Server S保持一條長連接,或者週期性連上Server S,以便能夠接收Server S的相關指令,我們稱這兩個連接分別爲ConnectA1,ConnectB1.

8.2.2.1網絡拓撲類型一

如下圖所示,Client A 位於NAT內網,而Client B是具有公網IP的機器。如果是Client A需要連接Client B那麼Client A直接連Client B就可以了。如果Client B需要連接Client A,那麼Client B直接Connect Client A一般是連接不上的。但是我們可以反過來讓Client A主動去連Client B不就可以了。下面所說的Client A或Client B的NAT類型指的是對於Server S能看到的Client的最外層的NAT的類型。
在這裏插入圖片描述
反過來讓Client A主動去連Client B的技術就是所謂的:反向連接技術。

具體的穿越過程如下:

1)Client B通過ConnectB1向Server S發送請求,請求連接Client A;
2)Server S按需回覆看是否需要啓動Client B的NAT類型偵測。(這要看Server S是否已經緩存了Client B的相關NAT信息);
3)Server S通過[2]可以知道Client B具有公網IP,於是,Server S通過ConnectA1發送指令給Client A讓Client A主動連Client B並告訴Client A目標Client B的IPB和監聽端口PortB;
4)Client A收到Client B的IPB和監聽端口PortB,然後發送連接請求連上Cient A並附帶一下身份信息,於是兩者就可以進行通信。

下面爲了描述簡便,具體的NAT偵測步驟就省略了。

8.2.2.2網絡拓撲類型二:

如下圖,Client A和Client B位於同一個NAT後面,這個時候Client A和 Client B位於同一個局域網。

P2P技術詳解(三):P2P中的NAT穿越(打洞)方案詳解(進階分析篇)_3.png

具體的穿越過程如下:

1)Client A通過ConnectA1向Server S發送請求,請求連接Client B;
2)Server S發現Client A、B位於同一個NAT後面,於是返回Client A、Client B的公網EndpointGA、EndpointGB和內網EndpointPA、EndpointPB給Client A;
3)Client A收到後,知道Client B和自己位於同一個NAT裏面,於是直接連上Client B的內網EndpointPB進行通信。

在這裏插入圖片描述
具體的穿越過程如下:

1)Client A通過ConnectA1向Server S發送請求,請求連接Client B;
2)Server S發現Client A、B位於同一個NAT後面,於是返回Client A、Client B的公網EndpointGA、EndpointGB和內網EndpointPA、EndpointPB給Client A;
3)Client A收到後,知道Client B和自己位於同一個NAT裏面,於是直接連上Client B的內網EndpointPB進行通信。

8.2.2.3 網絡拓撲類型三

如下圖,Client A和Client B分別位於不同的NAT後面,這個時候Client A和 Client B位於獨立的局域網。

在這裏插入圖片描述
具體的穿越過程如下:

1)Client A通過ConnectA1向Server S發送請求,請求連接Client B;
2)Server S發現Client A、B位於獨立的NAT後面,也是通過ConnectA1返回給Client A、Client B的公網EndpointGA、EndpointGB和內網EndpointPA、EndpointPA給Client A。並且通過ConnectB1返回給Client A、Client B的公網EndpointGA、EndpointGB和內網EndpointPA、EndpointPB給Client B。

接下來的步驟和Client A、Client B的NAT類型密切相關,下面會分別就相應的組合進行介紹具體的過程步驟。

(1)Client A是任意類型NAT,Client B 是Full Cone NAT(Endpoint Independent Mapping和Endpoint-Independent Filtering)

Full Cone NAT一般是比較少的,因爲這樣的NAT安全性很差。

[3] Server S通過ConnectA1發送指令讓Client A直接Connect Client B的外網EndpointGB,由於Client B的NAT是Full Cone,於是NAT不管三七二十一就把收到的包轉發給Client B,於是它們就可以順利通信了。

(2)Client A是任意類型NAT,Client B 是Restricted Cone NAT(Endpoint Independent Mapping和Address-Dependent Filtering)

[3] Server S通過ConnectB1發送指令讓Client B 先bind內網EndpointPB然後往Client A的外網EndpointGA發送Connect請求(由於Client B是Endpoint Independent Mapping,那麼EndpointPB依舊是映射爲EndpointGB),如果連接建立成功,那麼它們就可以進行通信了,反之失敗的話,Client B將失敗結果反饋給Server S,然後轉入[4];

[4] Server S收到失敗反饋,通過ConnectA1發送指令讓Client A往Client B的外網EndpointGB發送Connect請求,由於在步驟[3],Client B已經往Client A發送過數據包,根據過濾規則(Address-Dependent Filtering),Client B的NAT會允許Client A的數據包通過NAT並轉發給Client B。於是,它們就建立其連接進行通信。

(3)Client A的NAT類型:映射規則是(Endpoint Independent Mapping)的,過濾規則任意;Client B 是Port Restricted Cone NAT(Endpoint Independent Mapping和Address and Port-Dependent Filtering)

[3] 該步驟和情況(2)中的步驟[3]完全一樣。

[4] Server S收到失敗反饋,通過ConnectA1發送指令讓Client A 先bind內網EndpointPA然後往Client B的外網EndpointGB發送Connect請求(由於Client A是Endpoint Independent Mapping,那麼EndpointPA依舊是映射爲EndpointGA),由於在步驟[3],Client B已經往Client A的EndpointGA發送過數據包,根據過濾規則(Address and Port-Dependent Filtering),Client B的NAT會允許Client A的EndpointGA的數據包通過NAT並轉發給Client B。於是,它們就建立其連接進行通信。

(4)Client A的NAT類型:映射規則是(非Endpoint Independent Mapping)的,過濾規則任意;Client B 是Port Restricted Cone NAT(Endpoint Independent Mapping和Address and Port-Dependent Filtering)

在這種情況下,在上面的步驟[4]的時候,由於Client A是非Endpoint Independent Mapping,那麼EndpointPA就會映射爲是EndpointGA_B而不是EndpointGA了。這樣根據過濾 規則(Address and Port-Dependent Filtering),Client B的NAT將不會允許Client A的EndpointGA_B的數據包通過NAT。要想數據包能通過Client B的NAT,需要Client B曾經給EndpointGA_B發送過數據。但是,我們無法通過直接的方法讓Client B提前知道Client A的外網EndpointGA_B,難道就無能爲力了嗎?不,還是有些方法的,雖然無法直接知道Client A的外網EndpointGA_B,但是我們可以進行預測。

具體過程如下:

[3] 該步驟和情況(2)中的步驟[3]完全一樣。

[4] Server S收到失敗反饋,通過ConnectA1發送指令讓Client A 啓動端口映射預測過程。端口映射預測可以簡單、可以複雜,大體就是讓Client A往Server的不同端口、不同ip發送數據包,以便Server收集到Client A的端口映射樣本,以便能夠根據樣本的端口映射變化規律預測Client A的NAT的Mapping規則。

[5] Server S根據[4]的預測情況,通過ConnectB1發送給Client B接下來Client A可能的映射端口列表也就是可能的外網EndpointGA1、EndpointGA2 … EndpointGAn,然後讓Client B都往這些外網EndpointGA1、EndpointGA2 … EndpointGAn發送數據包。

[6] 然後Server S通過ConnectA1發送指令讓Client A 先bind內網EndpointPA然後往Client B的外網EndpointGB發送Connect請求(這個時候,假設預測算法有效的話,那麼Client A的內網EndpointPA將會映射爲EndpointGAi),由於在步驟[5],Client B已經往Client A的EndpointGAi發送過數據包,根據過濾規則(Address and Port-Dependent Filtering),Client B的NAT會允許Client A的EndpointGAi的數據包通過NAT並轉發給Client B。於是,它們就建立其連接進行通信。

[7] 如果在步驟[4]的預測失敗,那麼在步驟[6]將建立連接失敗,然後Client B將失敗結果反饋給Server S。這個時候Server S可以啓動重試步驟[4][5][6]或直接判斷Client A和Client B無法建立直接的P2P通信了,於是進入Relay(服務器中轉)環節。Realy部分在後面會單獨介紹。

8.2.2.4網絡拓撲類型四

如下圖,Client A和 Client B位於多層NAT後面。
在這裏插入圖片描述
具體過程如下:

1)Client A通過ConnectA1向Server S發送請求,請求連接Client B;
2)Server S發現Client A、B位於同一個NAT後面,於是返回Client A、Client B的公網EndpointGA、EndpointGB和內網EndpointPA、EndpointPB給Client A;
3)Client A收到後,認爲Client B和自己位於同一個NAT裏面,於是往Client B的內網EndpointPB發送連接請求,當然是連接不上的;
4)在連接失敗後,接着Client A嘗試向Client B的外網EndpointGB發送連接請求,這個時候NAT C收到數據包後是否轉發該數據包要看NAT C是否支持迴環轉換(hairpin translation),如果不支持那麼就無法進行直連P2P通信,需要就需要反饋給Server S開啓Relay。

5)在步驟4)失敗了,Client A是無法知道是因爲NAT C不支持迴環轉換造成的失敗,還是內層NAT的行爲造成的失敗。於是Client A就假設NAT C是支持迴環轉換的,這個時候網絡拓撲情況就變成網絡拓撲類型三了,那麼接下來的穿越步驟就和網絡拓撲類型三的多種情況一樣的了,這裏就不重複了。

上圖,只是給出了Client A、Client B位於兩層NAT後面的一種情況,對於多層NAT的各種組合本文就不介紹了。對於多層NAT的組合,在穿透失敗的時候,是比較難判斷出到底是哪層NAT的行爲造成的。我們只能用上面說過的所有方法進行逐一重試,如果還是失敗,那隻能啓動Relay進行服務器中轉了。

7、NAT穿越技術4:Relay服務器中轉技術

由於進行P2P穿透是否成功與NAT的行爲和防火牆策略有很大的關係,因此就算是一個P2P友好NAT也很難保證100%穿透成功。舉個例子:8.2.2.4 網絡拓撲類型四,假設NAT A、NAT B 、NAT C都是Full Cone NAT(完全錐型),但是如果NAT C不支持迴環轉換(hairpin translation)那麼也是無法穿透成功的。那麼一個完整的P2P穿透的解決方案必不可少的一個部分就是relay了,relay部分主要TURN協議描述。作爲STUN協議的一個補充,TURN協議主要由RFC5766、RFC6062、RFC6156來描述,其中RFC5766主要描述的是UDP協議的relay,RFC6062描述的是TCP協議(IPV4)的relay,而RFC6156描述的是IPV6的relay。下面主要介紹一下RFC5766和RFC6062兩個文檔中描述的較爲重要的交互過程,具體的協議相關屬性、報文結構等等,有興趣的可以細讀一下協議文檔。

TURN協議簡單的來講,如下圖所示:client向turn server發送一個Allocation request請求一個分配(allocation),如果turn server接收請求就會給client分配一個relay地址(IP_RELAYA: PORT_RELAYA),每個allocation都有一個有效期,過了有效期就不能使用了。在有效期內client可以發送refresh request來刷新延長有效期。Client A想給peer A發送數據需要創建權限,這個通過createPermission request請求來創建權限,權限創建成功後,client A就可以發送數據給turn server由turn server中轉給peer A,同時peer A發送給turn server數據也會被turn server中轉給client A。如圖中所示,由於client 沒有註冊peer B的權限,那麼client 發給peer B的數據會被turn server丟棄,同時peer B發給client 的數據也會被turn server丟棄。

在這裏插入圖片描述
在這裏插入圖片描述

7.1、UDP協議的Relay

首先介紹RFC5766,UDP協議的relay,主要有兩種方式:第一種是Send and Data methods,第二種是channels。下面分別介紹這兩個方式。

7.1.1 方式一、Send and Data methods,具體交互過程如下:

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
(1)首先client發送Allocate request 給TURN server 請求一個分配。其中攜帶的主要屬性:

Transaction-Id=0xA56250D3F17ABE679422DE85 :事務ID用於標識一個交互過程
SOFTWARE=“Example client, version 1.03” :可有可無的屬性
LIFETIME=3600 (1 hour) :請求分配的有效期,期望有效期
REQUESTED-TRANSPORT=17 (UDP) :未來數據傳輸採用的協議
DONT-FRAGMENT :請求不要將數據進行分割分包轉發給PEER。

(2)TURN server回覆一個Allocate error response響應,表示請求未通過授權,需要進行用戶驗證:

Transaction-Id=0xA56250D3F17ABE679422DE85 :事務ID要和(1)的一樣
SOFTWARE=“Example server, version 1.17” :可有可無
ERROR-CODE=401 (Unauthorized) :錯誤碼
REALM=“example.com” :爲了讓客戶端下次請求的時候要帶上這個屬性
NONCE=“adl7W7PeDU4hKE72jdaQvbAMcr6h39sm” :爲了讓客戶端下次請求的時候要帶上這個屬性

(3)Client收到響應後,發現是401錯誤響應,那麼需要給TURN server提供用戶名和密碼進行驗證。於是client重新發送Allocate request請求:

Transaction-Id=0xC271E932AD7446A32C234492 :另起一個事務,標識另外一個請求過程
SOFTWARE=“Example client 1.03” :同(1)
LIFETIME=3600 (1 hour) :同(1)
REQUESTED-TRANSPORT=17 (UDP) :同(1)
DONT-FRAGMENT :同(1)
USERNAME=“George” :client的用戶名
REALM=“example.com” :(2)中TURN server響應給client的
NONCE=“adl7W7PeDU4hKE72jdaQvbAMcr6h39sm” :(2)中TURN server響應給client的
MESSAGE-INTEGRITY=… :一些加密信息,用於驗證client的

(4)TURN server 驗證client通過後給client響應Allocate success response:

Transaction-Id=0xC271E932AD7446A32C234492 :事務ID要和(3)相同
SOFTWARE=“Example server, version 1.17” :同(3)
LIFETIME=1200 (20 minutes) :該分配的有效期,實際有效期
XOR-RELAYED-ADDRESS=192.0.2.15:50000 :給client分配的relay地址
XOR-MAPPED-ADDRESS=192.0.2.1:7000 :client的經NAT後的映射地址
MESSAGE-INTEGRITY=… :一些加密信息

(5)收到TURN server的success響應後,client發送CreatePermission request來創建peer的權限:

Transaction-Id=0xE5913A8F460956CA277D3319 :另起一個事務,標識另外一個請求過程
XOR-PEER-ADDRESS=192.0.2.150:0 :需要創建權限的peer的IP地址,權限只與IP地址相關,與端口無關
USERNAME=“George”
REALM=“example.com” :(2)中TURN server響應給client的
NONCE=“adl7W7PeDU4hKE72jdaQvbAMcr6h39sm” :(2)中TURN server響應給client的
MESSAGE-INTEGRITY=… :一些加密的信息

(6)TURN server接受創建權限請求,發送CreatePermission success resp 響應給client:

Transaction-Id=0xE5913A8F460956CA277D3319 :事務ID要和(5)相同
MESSAGE-INTEGRITY=… :一些加密信息

(7)創建權限成功後,client就可以用Send indication來發送數據給TURN server然後由TURN server將數據relay給peer:

Transaction-Id=0x1278E9ACA2711637EF7D3328 :另起一個事務,標識另外一個請求過程
XOR-PEER-ADDRESS=192.0.2.150:32102 :需要發送數據的peer監聽的IP: PORT(注意IP一定要和註冊權限的時候的IP一樣,否則會被拒絕relay並響應錯誤)
DONT-FRAGMENT :請求TURN server不要將data數據分片發送
DATA=… :client需要發給peer的數據內容

(8)TURN server收到Send indication請求後,進行一些權限檢查後,提取出協議包中的data屬性中的數據內容,然後將數據內容用UDP協議從client的relay地址(源:192.0.2.15:50000)發送給peer(目的:192.0.2.150:32102):

– UDP dgm ->
data=… : 發給peer的UDP 數據包

(9)peer收到UPD數據包後,如果有響應數據,那麼就將響應數據用UDP發給TURN server的192.0.2.15:50000地址:

<- UDP dgm –
data=… :響應給TURN server的UDP數據包

(10)TURN server在client的relay地址(192.0.2.15:50000)那收到peer(192.0.2.150:32102)的UDP數據包,這時TURN server需要檢測client是否註冊了IP192.0.2.150的權限,如果沒有就會丟棄該數據包。如果有那麼就取出UDP數據包中的data部分,然後將data封裝成TURN協議數據包,給client發送Data indication:

Transaction-Id=0x8231AE8F9242DA9FF287FEFF :協議並不要求這個事務ID要和(7)中的一樣
XOR-PEER-ADDRESS=192.0.2.150:32102 :標識數據來自哪個peer
DATA=… : peer 發給client的數據內容

以上是Send and Data methods方式的核心交互過程,較爲完整交互過程可以查看一下協議文檔。這裏有個問題需要說明一下,就是每個allocation都有一個有效期,client需要把握好有效期,及時在有效期內發送refresh request來刷新延長有效期。

7.1.2 方式二、channels,具體交互過程如下

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
(1)–(6)交互過程和Send and Data methods方式是一樣的,這裏就不在重複了。

(7)權限創建成功後,client發送ChannelBind request給TURN server請求進行channel bind。

Transaction-Id=0x6490D3BC175AFF3D84513212 :事務ID
CHANNEL-NUMBER=0x4000 :client定義的bind channel ID
XOR-PEER-ADDRESS=192.0.2.210:49191 :peer B的IP和PORT
USERNAME=“George” :同方式一
REALM=“example.com” :同方式一
NONCE=“adl7W7PeDU4hKE72jdaQvbAMcr6h39sm” :同方式一
MESSAGE-INTEGRITY=… :同方式一

(8)TURN server接受channelBind請求後,給client發送ChannelBind success response響應

Transaction-Id=0x6490D3BC175AFF3D84513212 :事務ID,和(7)相同
MESSAGE-INTEGRITY=… |

(9)client收到ChannelBind success response後就可以通過ChannelData來發送數據了。

Channel-number=0x4000 :(7)中定義bind channel ID
Data=… :client需要發給peer B的數據內容

(10)TURN server收到ChannelData後首先從TURN協議數據包中提取出Channel-number,接着查找Channel-number是否已經綁定peer,如果沒有就返回錯誤並丟棄數據包;如果查找到有綁定peer,那麼就提前出Data屬性中的數據內容用UDP協議通過client的relay地址(源:192.0.2.15:50000)發送給peer B(目的:192.0.2.210:49191)。

— UDP datagram --------->
Data=… 發給peer B的UDP 數據包

(11)peer收到UPD數據包後,如果有響應數據,那麼就將響應數據用UDP發給TURN server的192.0.2.15:50000地址

<-- UDP datagram ----------
Data=… :peer 發給client的數據內容

(12)TURN server在client的relay地址(192.0.2.15:50000)那收到peer(192.0.2.210:49191)的UDP數據包,這時TURN server需要檢測client是否註冊了IP192.0.2.150的權限,如果沒有就會丟棄該數據包。如果有註冊權限,那就檢查client是有channel綁定該peer,如果有那麼就通過Channel Data 方式relay數據給client,否則就通過方式一中的Data indication 方式relay數據給client

Channel-number=0x4000 :(7)中定義bind channel ID
Data=… :peer B發給client的數據內容

以上是channels的核心交互過程,較爲詳細的過程可以查看協議文檔。方式二比方式一多了一個channel Bind的步驟,這個步驟是爲了告訴TURN server接下來以Channel-number標識的協議數據包是要發給誰的,這樣才使得ChannelData中只要攜帶一個Channel-number頭部信息就可以,而不用攜帶方式一中的Transaction-Id、XOR-PEER-ADDRESS等額外的頭部信息,減少數據量。

7.2 TCP協議的Relay

TCP協議的relay是在RFC6062中描述,其中主要有兩種情況下的relay:1. Client to peer 2. Client to client。下面分別介紹兩種情況下relay。

7.2.1 情況一、Client to peer,網絡拓撲如下

在這裏插入圖片描述
在上面的網絡拓撲下,有兩種方式的relay:1. TURN Client 主動發起的relay 2. TURN Peer主動發起的relay。下面分別介紹這兩種方式的交互過程。這裏Turn Client表示能夠理解TURN協議的主機,而Turn Peer表示普通的一般主機。

9.2.1.1TURN Client 主動發起的relay:

在這種方式下,TURN server要能夠直接連接上TURN Peer監聽的端口才行。具體交互過程如下:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
(1)–(6)交互過程和RFC5766的是基本一樣的,這裏就不在重複了。所不同的是RFC5766中是UDP協議,而這裏是TCP協議,並且(1)–(6)是在一個連接中完成,我們稱這個連接爲control connection。

(7)client創建權限成功後,通過control connection發送Connect request給TURN server請求TURN server去連接Peer A

Transaction-Id=0x6490D3BC175AFF3D84513212 :事務ID
XOR-PEER-ADDRESS=192.0.2.210:49191 :Peer A監聽的IP和端口

(8)TURN server收到Connect request後, 它會通過client的relay地址(源:192.0.2.15:50000)嘗試TCP連接到Peer A的192.0.2.210:49191,如果連接不成功,那麼給client響應錯誤碼爲447的錯誤。如果連接成功那麼轉入(9),我們稱這個連接爲Peer data connection

(9)TURN server連接Peer A成功後,給client發送Connect success response

Transaction-Id=0x6490D3BC175AFF3D84513212 :事務ID,同(7)
CONNECTION-ID=0x123456787 :TURN server給client響應的標識,用於將兩條TCP連接聯繫起來用的。

(10)client在control connection上收到Connect success response,那麼client需要建立另外一條TCP連接連上TURN server,我們稱這條連接爲new connection。Client通過new connection給TURN server發送ConnectionBind request,請求將new connection和Peer data connection進行綁定。

Transaction-Id=0x6490D3BC175AFF3D84513212 :事務ID
CONNECTION-ID=0x123456787 :(9)中收到的CONNECTION-ID

(11)TURN server 收到ConnectionBind request後,進行一些操作,把new connection和Peer A connection兩條TCP連接聯繫起來。

通過上面11個步驟以後,client和peer A就能分別通過new connection和Peer data connection兩條TCP連接來發送數據了。Client通過new connection發送的數據到達TURN server,TURN server就會將數據原封不動通過Peer data connection轉發給Peer A,同樣對於Peer A也是一樣的,TURN server就像進行端口轉發一樣了。這裏有個問題是:Peer A connection這條TCP連接要比new connection這條TCP連接早一些建立起來的,這樣在new connection建立起來之前peer A就開始發送數據的話,那麼TURN server這個時候是無法將數據轉發給client的,所以RFC6062協議要求,只要Peer data connection連接建立好了,那麼TURN server就必須做好準備接收peer A的數據,並將接收到的數據buffer住,等new connection建立好後在轉發給client。但是,有些開源實現並沒有這樣做,所以這點要注意一下。

7.2.1.2 TURN Peer主動發起的relay

在這種方式下,TURN Peer可以位於NAT後面,具體交互如下:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
(1)–(6)交互過程和方式1的是一樣的,這裏就不在重複了。

(7)Peer A通過192.0.2.210:49191向client的relay地址192.0.2.15:50000發起TCP連接。TURN server 馬上accept這個TCP連接並做好buffer Peer A發送的數據流的準備。然後,TURN server檢查 擁有relay地址192.0.2.15:50000的client是否已經註冊了Peer A(192.0.2.210)的權限,如果沒有,那麼TURN server會馬上close剛剛accept的連接。如果有,那麼轉向(8),我們把這個連接稱爲peer data connection

(8)TURN server 查找到擁有relay地址192.0.2.15:50000的client的control connection,通過control connection給client發送ConnectionAttempt indication。

Transaction-Id=0x6490D3BC175AFF3D84513212 :事務ID
XOR-PEER-ADDRESS=192.0.2.210:49191 :(7)中accept那個peer A的IP和端口
CONNECTION-ID=0x789465213545 :TURN server給client響應的標識,用於將兩條TCP連接聯繫起來用的

(9)client收到ConnectionAttempt indication,如果接收這個peer的話,那麼client會新起一個連接連上TURN server,我們稱這個連接爲new connection,client通過new connection給TURN server發送ConnectionBind request,請求綁定peer data connection。

Transaction-Id=0x6490D3BC175AFF3D84513212 :事務ID
CONNECTION-ID=0x789465213545 : (8)中收到的CONNECTION-ID

(10)TURN server收到ConnectionBind request後會通過new connection給client發送ConnectionBind request success response。
通過上面10個步驟以後,client和peer A就能分別通過new connection和Peer data connection兩條TCP連接來發送數據了。這個方式同樣存在方式1中的數據buffer住問題。在這種情況下,在Peer A看來與之通信的是Endpoint(client的relay地址192.0.2.15:50000),Peer A不需要知道真實的Client的地址。

7.2.2 情況二、Client to client,網絡拓撲結構如下

在這裏插入圖片描述
這種情況下,RFC6062文檔中並沒有講到,估計是因爲這種情況是情況一的一個特例而已,我這裏展開來講一下是希望能幫助大家更加深刻理解協議本身。

TURN Client1和TURN Client2(1)-(6)步驟的交互情況基本和上面的一樣,並且是比較獨立的,所以下面直接給出了

TURN Client1 的(1)-(6)步驟交互情況如下:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
從上面的交互可以知道TURN Client 1 的relay地址是:192.0.2.15:50000 ,NAT映射後的地址是:192.0.2.1:7000,而TURN Client2的relay地址是:192.0.2.150:40000 ,NAT映射後的地址是:192.0.2.2:7000。下面繼續給出TURN Client 1和TURN Client 2的其他交互情況,由於它們和TURN server的交互帶有一定的時序性,下面會交錯給出它們和TURN server的交互步驟。TURN Client1和TURN Client2是對稱,這裏不妨假設TURN Client1是數據交互的發起者,具體交互過程如下:
在這裏插入圖片描述
(7)TURN Client1 首先通過control connection1發送Connect request給TURN server,請求連接TURN Client 2的relay地址192.0.2.150:40000。

Transaction-Id=0x6490D3BC175AFF3D84513212 :事務ID
XOR-PEER-ADDRESS=192.0.2.150:40000 : TURN Client2的relay地址

(8)TURN server收到Connect request後, 它會通過TURN Client1的relay地址(源:192.0.2.15:50000)嘗試TCP連接到192.0.2.150:40000,這個連接一般都會成功,因爲這個是TURN server給的relay地址,我們稱這個連接爲peer data connection1

(9)TURN server連接192.0.2.150:40000成功後,給TURN Client1發送Connect success response

Transaction-Id=0x6490D3BC175AFF3D84513212:事務ID
CONNECTION-ID=0x123456787 :TURN server給client響應的標識,用於將兩條TCP連接聯繫起來用的

(10)這個步驟和(9)幾乎同時發生的,TURN server發現TURN Client2的relay地址192.0.2.150:40000有個TCP連接上來,那麼TURN server馬上accept這個連接,我們稱這個連接是peer data connection2(其實就是peer data connection1);經過權限檢查後,TURN server通過TURN Client2的control connection2給TURN Client2發送ConnectionAttempt indication

Transaction-Id=0x6490D3BC175AFF3D84511111 :事務ID
XOR-PEER-ADDRESS=192.0.2.15:50000 :TURN Client1的relay地址
CONNECTION-ID=0x789465213545 :TURN server給client響應的標識,用於將兩條TCP連接聯繫起來用的

(11)TURN Client1 收到Connect success response後,另起一個TCP connection連接上TURN server,我們稱這個連接爲new connection1。TURN Client1通過new connection1給TURN server 發送ConnectionBind request

Transaction-Id=0x6490D3BC175AFF3D84513212 :事務ID
CONNECTION-ID=0x123456787 : (9)中TURN server響應的CONNECTION-ID

(12)這個步驟幾乎和(11)同時發生,TURN Client2 收到ConnectionAttempt indication,表示接受,然後它另起一個TCP connection連接上TURN server我們稱這個連接爲new connection2。TURN Client2通過new connection2給TURN server發送ConnectionBind request。

Transaction-Id=0x6490D3BC175AFF3D84511111 :事務ID
CONNECTION-ID=0x789465213545 :(10)中TURN server響應的CONNECTION-ID

(13)和(14)TURN server分別通過new connection1和new connection2給TURN Client 1和TURN Client 2發送ConnectionBind request success response。

通過以上14個步驟,TURN Client 1就能借助new connection1和peer data connection1與TURN Client 2進行數據交互。而TURN Client 2藉助new connection2和peer data connection2與TURN Client 1進行數據交互。

到這裏,P2P通信穿越NAT的相關原理、技術、方法的進階分析基本介紹完畢,關於STUN和TURN協議,有個開源實現,有興趣的同學可以閱讀一下源碼:
https://github.com/coturn/rfc5766-turn-server
另外還有一個ICE協議,這個也有一個文檔系列:

RFC 5245 - ICE
RFC 5768 – ICE–SIP
RFC 6336 – ICE–IANA Registry
RFC 6544 – ICE–TCP
RFC 5928 - TURN Resolution Mechanism

這個文檔系列較爲複雜一些,有興趣的可以閱讀一下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章