http://blog.csdn.net/ustcgy/article/details/5655050
2010
5. NAT穿透
5.1 轉(zhuǎn)發(fā)
最可靠但又是最低效的點(diǎn)對(duì)點(diǎn)通信方法,莫過于將p2p網(wǎng)絡(luò)通信看作一個(gè)C/S結(jié)構(gòu),通過服務(wù)器來轉(zhuǎn)發(fā)信息.如下圖,兩個(gè)客戶端A和B,均與服務(wù)器S初始化了一個(gè)TCP或UDP連接,服務(wù)器S具有公網(wǎng)固定IP地址,兩個(gè)客戶端分布在不同的私網(wǎng)中,這樣,他們各自的NAT代理服務(wù)器將不允許他們進(jìn)行直連.
Server S
|
|
+----------------------+----------------------+
| |
NAT A NAT B
| |
| |
Client A Client B
取而代之的方式是,兩個(gè)客戶端可以把服務(wù)器S當(dāng)作信使來轉(zhuǎn)發(fā)消息.比如,為了將消息發(fā)送到B,A先發(fā)送一條信息給服務(wù)器S,服務(wù)器S再利用初始化時(shí)已經(jīng)建立的連接,將信息轉(zhuǎn)發(fā)給B.
這個(gè)方法的優(yōu)勢(shì)是:它適合于任何NAT包括Symmetric NAT.但是它的劣勢(shì)也很明顯:它將全面依賴并消耗服務(wù)器的資源和網(wǎng)絡(luò)帶寬.名為 TURN 的協(xié)議定義了一個(gè)利用轉(zhuǎn)發(fā)技術(shù)進(jìn)行可靠通信的模型.
5.2 反向連接
這里介紹第二種技術(shù),但是它只能在通信的兩端只有一端處于NAT之后的情況下.舉例來說,假設(shè)客戶端A處于NAT之后,而客戶端B有一個(gè)公網(wǎng)IP地址,如下圖所示.
Server S
18.181.0.31:1235
|
|
+----------------------+----------------------+
| |
NAT A |
155.99.25.11:62000 |
| |
| |
Client A Client B
10.0.0.1:1234 138.76.29.7:1234
現(xiàn)在我們假設(shè)客戶端B將會(huì)與客戶端A初始化一個(gè)端對(duì)端連接會(huì)話.B將首先試圖連接A的一個(gè)地址---客戶端A認(rèn)為是它自己的地址10.0.0.1:1234或者是從服務(wù)器S觀察到的地址155.99.25.11:62000.然而不論是連接哪一個(gè),都不可能成功.第一種情況:試圖直接連到10.0.0.1肯定會(huì)失敗,因?yàn)?0.0.0.1根本就不是一個(gè)可以在公網(wǎng)上路由的IP地址;第二種情況,從B傳來的請(qǐng)求將能夠到達(dá)端口NAT A的端口62000,但NAT A卻會(huì)拒絕這個(gè)連接請(qǐng)求,因?yàn)橹挥型獬龅倪B接才允許進(jìn)入. 在所有的嘗試都失敗之后,客戶端B就只能通過服務(wù)器S來請(qǐng)求A做一個(gè)"反向"連接到客戶端B,客戶端A將打開一個(gè)與客戶端B通訊的連接(在B的公網(wǎng)IP地址和端口號(hào)上).NAT A允許這個(gè)連接通過,因?yàn)檫@個(gè)連接起源于NAT A的內(nèi)部,并且同時(shí)客戶端B能夠受這個(gè)連接因?yàn)锽并不位于NAT之后.
這個(gè)方法的優(yōu)勢(shì)是:它也適合于任何NAT包括Symmetric NAT.它的主要限制在于,只能有一端位于NAT之后.
5.3 UDP打洞
第三種技術(shù),也是這篇文章主要要介紹的,就是非常有名的"UDP打洞技術(shù)".這里將考慮兩種典型場(chǎng)景,來介紹連接的雙方應(yīng)用程序如何按照計(jì)劃的進(jìn)行通信的,第一種場(chǎng)景,我們假設(shè)兩個(gè)客戶端都處于不同的NAT之后;第二種場(chǎng)景,我們假設(shè)兩個(gè)客戶端處于同一個(gè)NAT之后,但是它們彼此都不知道(他們?cè)谕粋€(gè)NAT中).
5.3.1 處于不同NAT之后的客戶端通信
我們假設(shè) Client A和Client B都擁有自己的私有IP地址,并且都處在不同的NAT之后,端對(duì)端的程序運(yùn)行于 CLIENT A,CLIENT B,S之間,并且它們都開放了UDP端口1234. CLIENT A和CLIENT B首先分別與S建立通信會(huì)話,這時(shí)NAT A把它自己的UDP端口62000分配給CLIENT A與S的會(huì)話,NAT B也把自己的UDP端口31000分配給CLIENT B與S的會(huì)話.如下圖所示:
Server S
18.181.0.31:1234
|
|
+----------------------+----------------------+
| |
NAT A NAT B
155.99.25.11:62000 138.76.29.7:31000
| |
| |
Client A Client B
10.0.0.1:1234 10.1.1.3:1234
假如這個(gè)時(shí)候 CLIENT A 想與 CLIENT B建立一條UDP通信直連,如果 CLIENT A只是簡(jiǎn)單的發(fā)送一個(gè)UDP信息到CLIENT B的公網(wǎng)地址
138.76.29.7:31000的話,NAT B會(huì)不加考慮的將這個(gè)信息丟棄(除非NAT B是一個(gè) full cone NAT),因?yàn)檫@個(gè)UDP信息中所包含的地址信息,與CLIENT B和服務(wù)器S建立連接時(shí)存儲(chǔ)在NAT B中的服務(wù)器S的地址信息不符.同樣的,CLIENT B如果做同樣的事情,發(fā)送的UDP信息也會(huì)被NAT A丟棄.
假如 CLIENT A 開始發(fā)送一個(gè)UDP信息到CLIENT B的公網(wǎng)地址上,與此同時(shí),他又通過S中轉(zhuǎn)發(fā)送了一個(gè)邀請(qǐng)信息給CLIENT B,請(qǐng)求CLIENT B也給CLIENT A發(fā)送一個(gè)UDP信息到 CLIENT A的公網(wǎng)地址上.這時(shí)CLIENT A向CLIENT B的公網(wǎng)IP(138.76.29.7:31000)發(fā)送的信息導(dǎo)致 NAT A 打開一個(gè)處于CLIENT A的私有地址和CLIENT B的公網(wǎng)地址之間的新的通信會(huì)話,與此同時(shí)NAT B也打開了一個(gè)處于CLIENT B的私有地址和CLIENT A的公網(wǎng)地址(155.99.25.11:62000)之間的新的通信會(huì)話.一旦這個(gè)新的UDP會(huì)話各自向?qū)Ψ酱蜷_了,CLIENT A和CLIENT B之間就可以直接通信,而無需S來牽線搭橋了.這就是所謂的打洞技術(shù).
一旦這種處于NAT之后的端對(duì)端的直連建立之后,連接的雙方可以輪流擔(dān)任對(duì)方的"媒人",把對(duì)方介紹給其他的客戶端,這樣就極大的降低了服務(wù)器S的工作量.
5.3.2 處于相同NAT之后的客戶端通信
我們假設(shè) Client A和Client B都擁有自己的私有IP地址,并且都處在相同的NAT之后,端對(duì)端的程序運(yùn)行于 CLIENT A,CLIENT B,S之間,
CLIENT A和CLIENT B分別與S建立通信會(huì)話,經(jīng)過NAT轉(zhuǎn)換后,A的公網(wǎng)端口被映射為62000,B的公網(wǎng)端口映射為62001.如下圖所示:
Server S
18.181.0.31:1234
|
|
NAT
A-S 155.99.25.11:62000
B-S 155.99.25.11:62001
|
+----------------------+----------------------+
| |
Client A Client B
10.0.0.1:1234 10.1.1.3:1234
根據(jù)前面介紹的"打洞"技術(shù),CLIENT A將發(fā)送一個(gè)UDP信息到CLIENT B的公網(wǎng)地址上,數(shù)據(jù)包源端為(10.0.0.1:124),目的端為(155.99.25.11:62001).該數(shù)據(jù)包能否被B收到,取決于當(dāng)前的NAT是否支持"發(fā)夾"轉(zhuǎn)換(hairpin轉(zhuǎn)換,也就是同一臺(tái)設(shè)備不同端口之間的UDP數(shù)據(jù)包能否到達(dá)).
首先,支持"發(fā)夾"轉(zhuǎn)換的NAT設(shè)備還遠(yuǎn)沒有支持"打洞"技術(shù)的NAT設(shè)備多,其次,即使NAT設(shè)備支持"發(fā)夾"轉(zhuǎn)換,在這種情況下也應(yīng)該通過網(wǎng)內(nèi)端到端實(shí)現(xiàn),而不是將數(shù)據(jù)包無謂 地經(jīng)過NAT設(shè)備,這是一種對(duì)資源的浪費(fèi).
5.3.3 一般"打洞"過程
綜合上面介紹的客戶端處于不同NAT之后和處于同一NAT之后,我們說下一般的"打洞"過程.
1. 打洞技術(shù)假定客戶端A和B可以與公網(wǎng)內(nèi)的已知的集中服務(wù)器建立UDP連接(可以互發(fā)UDP數(shù)據(jù)包).當(dāng)一個(gè)客戶端在S上登陸的時(shí)候,服務(wù)器記錄下該客戶端的兩個(gè)endpoints(IP地址,UDP端口),一個(gè)是該客戶端確信自己是通過該ip和端口與服務(wù)器S進(jìn)行通信的,另一個(gè)是服務(wù)器S記錄下的由服務(wù)器"觀察"到的該客戶端實(shí)際與自己通信所使用的ip和端口.我們可以把前一個(gè)endpoint看作是客戶端的內(nèi)網(wǎng)ip和端口,把后一個(gè)endpoint看作是客戶端的內(nèi)網(wǎng)ip和端口經(jīng)過NAT轉(zhuǎn)換后的公網(wǎng)ip和端口.服務(wù)器可以從客戶端的登陸消息的消息體中得到該客戶端的內(nèi)網(wǎng)endpoint相關(guān)信息,可以通過對(duì)登陸消息的IP或UDP頭得到該客戶端的公網(wǎng)endpoint.
2.假設(shè)Client A想向B發(fā)起連接,于是A向服務(wù)器S發(fā)送消息,請(qǐng)求S幫助建立與B的UDP連接.這時(shí),S將B的公網(wǎng)和內(nèi)網(wǎng)的endpoint發(fā)給A.可知,A與B通過與S的一次通信就可以知道對(duì)方的公網(wǎng)和內(nèi)網(wǎng)的endpoint.
3.Client A通過B的內(nèi)網(wǎng)endpoint發(fā)送UDP數(shù)據(jù)包.針對(duì)5.3.2節(jié)問題的解決方案.如果B和A在同一NAT后,則很快收到響應(yīng).如果B和A不在同一NAT后,則超時(shí).
4.Client A通過B的外網(wǎng)endpoint發(fā)送UDP數(shù)據(jù)包.
回到5.3.1節(jié)介紹的具體方法.CLIENT A發(fā)出UDP包(10.0.0.1:1234,138.76.29.7:31000),經(jīng)NAT A轉(zhuǎn)換為(155.99.25.11:62000,138.76.29.7:31000),經(jīng)NAT B轉(zhuǎn)換為(155.99.25.11:62000,10.1.1.3:1234).如果在此數(shù)據(jù)包到達(dá)NAT B前,B發(fā)送過UDP包到A的公網(wǎng)endpoint,則NAT B允許此包到達(dá)B機(jī).在5.3.2節(jié)下,A發(fā)出UDP包(10.0.0.1:1234,155.99.25.11:62001),NAT 先轉(zhuǎn)換為(155.99.25.11:62000,155.99.25.11:62001),再轉(zhuǎn)換為(155.99.25.11:62000,10.1.1.3:1234).
5.步驟3,4發(fā)送的數(shù)據(jù)包是為了"打洞",打洞成功后,就進(jìn)入真正的P2P傳輸了.
還有一種情況,B和A不在同一NAT后,C和A在同一NAT后,且B和C的內(nèi)網(wǎng)endpoint一致,這個(gè)時(shí)候A從S拿到的目的端應(yīng)該有2個(gè).所以針對(duì)3,4步驟取先有回應(yīng)的目的端不可取,應(yīng)該先做步驟3,有回應(yīng)直接到步驟5,沒有回應(yīng)到步驟4.
5.3.4 客戶端分別處于多層NAT之后
在有些網(wǎng)絡(luò)拓?fù)渲芯痛嬖诙鄬覰AT設(shè)備,讓我們來看看下圖這種情況:
假如 NAT X 是由 Internet服務(wù)供應(yīng)商(ISP)配置的一個(gè)大型NAT,它使用少量的公網(wǎng)IP地址來為一些客戶群提供服務(wù),NAT A和NAT B則是
為ISP的兩個(gè)客戶群所配置的小一點(diǎn)的獨(dú)立NAT網(wǎng)關(guān),它們?yōu)楦髯钥蛻羧旱乃饺思彝ゾW(wǎng)絡(luò)提供IP地址.只有Server S和NAT X擁有公網(wǎng)固定IP地址,而NAT A 和 NAT B所擁有的"公網(wǎng)"IP地址對(duì)于ISP的尋址域來說則實(shí)際上"私有"的.
Server S
18.181.0.31:1234
|
|
NAT X
A-S 155.99.25.11:62000
B-S 155.99.25.11:62001
|
|
+----------------------+----------------------+
| |
NAT A NAT B
192.168.1.1:30000 192.168.1.2:31000
| |
| |
Client A Client B
10.0.0.1:1234 10.1.1.3:1234
現(xiàn)在讓我們假設(shè)Client A和Client B想要建立一條端對(duì)端 的UDP直連.Client A和 Client B只知道Server S記錄的他們真正的公網(wǎng)地址
155.99.25.11:62000和155.99.25.11:62001,而且他們只能通過這個(gè)公網(wǎng)地址建立連接,即NAT X必須得支持"loopback translation"(也稱hairpin轉(zhuǎn)換)才行.
Client A和 Client B也知道對(duì)方內(nèi)網(wǎng)地址10.0.0.1:1234和10.1.1.3:1234,毫無疑問,通過內(nèi)網(wǎng)地址是建立不了連接的.Client A和 Client B并不知道對(duì)方NAT B和NAT A的地址192.168.1.2:31000和192.168.1.1:30000,即便假設(shè)我們通過某種途徑得知了這些地址,還是不能夠保證這樣就能進(jìn)行通話了,因?yàn)檫@些地址是由ISP的私有尋址域分配的,可能會(huì)與私有域所分配的其他無關(guān)客戶端地址相沖突.
5.3.5 UDP在空閑狀態(tài)下的超時(shí)問題
由于UDP轉(zhuǎn)換協(xié)議提供的"洞"不是絕對(duì)可靠的,多數(shù)NAT設(shè)備內(nèi)部都有一個(gè)UDP轉(zhuǎn)換的空閑狀態(tài)計(jì)時(shí)器,如果在一段時(shí)間內(nèi)沒有UDP數(shù)據(jù)通信,NAT設(shè)備會(huì)關(guān)掉由"打洞"操作打出來的"洞",做為應(yīng)用程序來講如果想要做到與設(shè)備無關(guān),就最好在穿越NAT的以后設(shè)定一個(gè)穿越的有效期.很遺憾目前沒有標(biāo)準(zhǔn)有效期,這個(gè)有效期與NAT設(shè)備內(nèi)部的配置有關(guān),最短的只有20秒左右.在這個(gè)有效期內(nèi),即使沒有p2p數(shù)據(jù)包需要傳輸,應(yīng)用程序?yàn)榱司S持該"洞"可以正常工作,也必須向?qū)Ψ桨l(fā)送"打洞"維持包.這個(gè)維持包是需要雙方應(yīng)用都發(fā)送的,只有一方發(fā)送不會(huì)維持另一方的session正常工作.除了頻繁發(fā)送"打洞"維持包以外,還有一個(gè)方法就是在當(dāng)前的"洞"有效期過期之前,p2p客戶端雙方重新"打洞",丟棄原有的"洞",這也不失為一個(gè)有效的方法.
5.4 . UPD端口號(hào)預(yù)言
在使用"UDP打洞技術(shù)"時(shí)有一點(diǎn)必須要注意:它只能在雙方的NAT都是cone NAT時(shí)才能正常工作.這些NAT在使用時(shí)保持著端口的
綁定----[私有IP,私有UDP端口]對(duì)和[公網(wǎng)IP,公網(wǎng)UDP端口]對(duì)的一一對(duì)應(yīng).
如果像 symmetricNAT那樣給每個(gè)新的會(huì)話都分配一個(gè)新的公網(wǎng)端口,那么UDP應(yīng)用程序想要與其他外部客戶端進(jìn)行通話,就無法重復(fù)使用已經(jīng)建立好的通信轉(zhuǎn)換.
讓我們來考慮這樣一種情況,有兩個(gè)客戶端A和B,他們都藏在不同的Symmetric NAT后面,他們都開放了一個(gè)UDP連接給具有固定IP的Server S.如下圖:
Server S
18.181.0.31:1234
|
|
+----------------------+----------------------+
| |
Symmetric NAT A Symmetric NAT B
A-S 155.99.25.11:62000 B-S 138.76.29.7:31000
| |
| |
Client A Client B
10.0.0.1:1234 10.1.1.3:1234
NAT A 分配了它自己的UDP端口62000,用來保持客戶端A與服務(wù)器S的通信會(huì)話,NAT B 也分配了31000端口,用來保持客戶端B與
服務(wù)器S的通信會(huì)話.通過與服務(wù)器S的對(duì)話,客戶端A和客戶端B都相互知道了對(duì)方所映射的真實(shí)IP和端口.
客戶端A發(fā)送一條UDP消息到 138.76.29.7:31001(請(qǐng)注意到端口號(hào)的增加),同時(shí)客戶端B發(fā)送一條UDP消息到 155.99.25.11:62001.
如果NAT A 和NAT B繼續(xù)分配端口給新的會(huì)話,并且從A-S和B-S的會(huì)話時(shí)間消耗得并不多的話,那么一條處于客戶端A和客戶端B之間的雙向會(huì)話通道就建立了.
客戶端A發(fā)出的消息送達(dá)B導(dǎo)致了NAT A打開了一個(gè)新的會(huì)話,并且我們希望 NAT A將會(huì)指派62001端口給這個(gè)新的會(huì)話,因?yàn)?2001是
繼62000后,NAT會(huì)自動(dòng)指派給 從服務(wù)器S到客戶端A之間的新會(huì)話的端口號(hào);類似的,客戶端B發(fā)出的消息送達(dá)A導(dǎo)致了NAT B打開了
一個(gè)新的會(huì)話,并且我們希望 NAT B將會(huì)指派31001這個(gè)端口給新的會(huì)話;如果兩個(gè)客戶端都正確的猜測(cè)到了對(duì)方新會(huì)話被指派的端口號(hào),
那么這個(gè)客戶端A----客戶端B的雙向連接就被打通了.其結(jié)果如下圖所示:
Server S
18.181.0.31:1234
|
|
+----------------------+----------------------+
| |
NAT A NAT B
A-S 155.99.25.11:62000 B-S 138.76.29.7:31000
A-B 155.99.25.11:62001 B-A 138.76.29.7:31001
| |
| |
Client A Client B
10.0.0.1:1234 10.1.1.3:1234
明顯的,有許多因素會(huì)導(dǎo)致這個(gè)方法失敗:如果這個(gè)預(yù)言的新端口(62001和31001) 恰好已經(jīng)被一個(gè)不相關(guān)的會(huì)話所使用,那么NAT就會(huì)跳過這個(gè)端口號(hào),這個(gè)連接就會(huì)宣告失敗;如果兩個(gè)NAT有時(shí)或者總是不按照順序來生成新的端口號(hào),那么這個(gè)方法也是行不通的.如果隱藏在NAT A后的一個(gè)不同的客戶端X(或者在NAT B后)打開了一個(gè)新的"外出"UDP 連接,并且無論這個(gè)連接的目的如何,只要這個(gè)動(dòng)作發(fā)生在客戶端A建立了與服務(wù)器S的連接之后,客戶端A與客戶端B建立連接之前,那么這個(gè)無關(guān)的客戶端X 就會(huì)趁人不備地"偷"到這個(gè)我們渴望分配的端口.所以,這個(gè)方法變得如此脆弱而且不堪一擊,只要任何一個(gè)NAT方包含以上碰到的問題,這個(gè)方法都不會(huì)奏效.
最后,如果P2P的一方處在兩級(jí)或者兩級(jí)以上的NAT下面,并且這些NAT接近這個(gè)客戶端是symmetric的話,端口號(hào)預(yù)言是無效的.
因此,并不推薦使用這個(gè)方法來寫新的P2P應(yīng)用程序.
5.5 同時(shí)開放TCP連接
這里有一種方法能夠在某種情況下建立一個(gè)穿透NAT的端對(duì)端TCP直連.我們知道,絕大多數(shù)的TCP會(huì)話的建立,都是通過一端先發(fā)送一個(gè)SYN包開始,另一方則回發(fā)一個(gè)SYN-ACK包的過程.然而,這里確實(shí)存在另外一種情況,就是P2P的雙方各自同時(shí)地發(fā)出一個(gè)SYN包到對(duì)方的公網(wǎng)地址上,然后各自都單獨(dú)地返回一個(gè)ACK響應(yīng)來建立一個(gè)TCP會(huì)話.這個(gè)過程被稱之為"Simultaneous open"(同時(shí)開放連接).
如果一個(gè)NAT接收到一個(gè)來自私有網(wǎng)絡(luò)外面的TCP SYN包,這個(gè)包想發(fā)起一個(gè)"引入"的TCP連接,一般來說,NAT會(huì)拒絕這個(gè)連接請(qǐng)求并扔掉這個(gè)SYN 包,或者回送一個(gè)TCP RST(connection reset,重建連接)包給請(qǐng)求方.但是,有一種情況,當(dāng)這個(gè)接收到的SYN包中的源IP地址和端口,目標(biāo)IP地址和端口都與NAT登記的一個(gè)已經(jīng)激活的TCP會(huì)話中的地址信息相符時(shí),NAT將會(huì)放行這個(gè)SYN 包,讓它進(jìn)入NAT內(nèi)部.特別要指出,如果NAT恰好看到一個(gè)剛剛發(fā)送出去的一個(gè)SYN包也和上面接收到的SYN包中的地址信息相符合的話,那么NAT將會(huì)認(rèn)為這個(gè)TCP連接已經(jīng)被激活,并將允許這個(gè)方向的SYN包進(jìn)入NAT內(nèi)部.
如果Client A和Client B能夠彼此正確的預(yù)知對(duì)方的NAT將會(huì)給下一個(gè)TCP連接分配的公網(wǎng)TCP端口,并且兩個(gè)客戶端能夠同時(shí)地發(fā)起一個(gè)"外出"的TCP連接,并在對(duì)方的SYN包到達(dá)之前,自己剛發(fā)送出去的SYN包都能順利的穿過自己的NAT的話,一條端對(duì)端的TCP連接就成功地建立了.
不幸的是,這個(gè)詭計(jì)比5.4節(jié)所講的UDP端口預(yù)言更容易被粉碎,并且對(duì)時(shí)間的敏感性的依賴更多.
這個(gè)屬于TCP穿透,它同樣基于NAT對(duì)TCP穿透的支持,基本原理和UDP穿透一致.具體實(shí)現(xiàn)后面會(huì)繼續(xù)介紹.
聯(lián)系客服