第六章 p2p原理與實(shí)踐
當(dāng)今互聯(lián)網(wǎng)到處存在著一些中間件(MIddleBoxes),如NAT和防火墻,導(dǎo)致兩個(gè)(不在同一內(nèi)網(wǎng))中的客戶端無法直接通信。 這些問題即便是到了IPV6時(shí)代也會(huì)存在,因?yàn)榧词共恍枰狽AT,但還有其他中間件如防火墻阻擋了鏈接的建立。 目前部署的中間件多都是在C/S架構(gòu)上設(shè)計(jì)的,其中相對(duì)隱匿的客戶機(jī)主動(dòng)向周知的服務(wù)端(擁有靜態(tài)IP地址和DNS名稱)發(fā)起鏈接請(qǐng)求。 大多數(shù)中間件實(shí)現(xiàn)了一種非對(duì)稱的通訊模型,即內(nèi)網(wǎng)中的主機(jī)可以初始化對(duì)外的鏈接,而外網(wǎng)的主機(jī)卻不能初始化對(duì)內(nèi)網(wǎng)的鏈接, 除非經(jīng)過中間件管理員特殊配置。
在中間件為常見的NAPT的情況下(也是本文主要討論的),內(nèi)網(wǎng)中的客戶端沒有單獨(dú)的公網(wǎng)IP地址, 而是通過NAPT轉(zhuǎn)換,和其他同一內(nèi)網(wǎng)用戶共享一個(gè)公網(wǎng)IP。這種內(nèi)網(wǎng)主機(jī)隱藏在中間件后的不可訪問性對(duì)于一些客戶端軟件如瀏覽器來說 并不是一個(gè)問題,因?yàn)槠渲恍枰跏蓟瘜?duì)外的鏈接,從某方面來看反而還對(duì)隱私保護(hù)有好處。然而在P2P應(yīng)用中, 內(nèi)網(wǎng)主機(jī)(客戶端)需要對(duì)另外的終端(Peer)直接建立鏈接,但是發(fā)起者和響應(yīng)者可能在不同的中間件后面, 兩者都沒有公網(wǎng)IP地址。而外部對(duì)NAT公網(wǎng)IP和端口主動(dòng)的鏈接或數(shù)據(jù)都會(huì)因內(nèi)網(wǎng)未請(qǐng)求被丟棄掉。本文討論的就是如何跨越NAT實(shí)現(xiàn)內(nèi)網(wǎng)主機(jī)直接通訊的問題。
1. 術(shù)語
防火墻(Firewall): 防火墻主要限制內(nèi)網(wǎng)和公網(wǎng)的通訊,通常丟棄未經(jīng)許可的數(shù)據(jù)包。防火墻會(huì)檢測(cè)(但是不修改)試圖進(jìn)入內(nèi)網(wǎng)數(shù)據(jù)包的IP地址和TCP/UDP端口信息。
網(wǎng)絡(luò)地址轉(zhuǎn)換器(NAT): NAT不止檢查進(jìn)入數(shù)據(jù)包的頭部,而且對(duì)其進(jìn)行修改,從而實(shí)現(xiàn)同一內(nèi)網(wǎng)中不同主機(jī)共用更少的公網(wǎng)IP(通常是一個(gè))。
基本NAT(Basic NAT): 基本NAT會(huì)將內(nèi)網(wǎng)主機(jī)的IP地址映射為一個(gè)公網(wǎng)IP,不改變其TCP/UDP端口號(hào)?;綨AT通常只有在當(dāng)NAT有公網(wǎng)IP池的時(shí)候才有用。
網(wǎng)絡(luò)地址-端口轉(zhuǎn)換器(NAPT): 到目前為止最常見的即為NAPT,其檢測(cè)并修改出入數(shù)據(jù)包的IP地址和端口號(hào),從而允許多個(gè)內(nèi)網(wǎng)主機(jī)同時(shí)共享一個(gè)公網(wǎng)IP地址。
錐形NAT(Cone NAT): 在建立了一對(duì)(公網(wǎng)IP,公網(wǎng)端口)和(內(nèi)網(wǎng)IP,內(nèi)網(wǎng)端口)二元組的綁定之后,Cone NAT會(huì)重用這組綁定用于接下來該應(yīng)用程序的所有會(huì)話(同一內(nèi)網(wǎng)IP和端口),只要還有一個(gè)會(huì)話還是激活的。 例如,假設(shè)客戶端A建立了兩個(gè)連續(xù)的對(duì)外會(huì)話,從相同的內(nèi)部端點(diǎn)(10.0.0.1:1234)到兩個(gè)不同的外部服務(wù)端S1和S2。Cone NAT只為兩個(gè)會(huì)話映射了一個(gè)公網(wǎng)端點(diǎn)(155.99.25.11:62000), 確??蛻舳硕丝诘摹吧矸荨痹诘刂忿D(zhuǎn)換的時(shí)候保持不變。由于基本NAT和防火墻都不改變數(shù)據(jù)包的端口號(hào),因此這些類型的中間件也可以看作是退化的Cone NAT。
? ? Server S1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Server S2
18.181.0.31:1235? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 138.76.29.7:1235
? ? ? |? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? |? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? +----------------------+----------------------+
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ^? Session 1 (A-S1)? ^? ? ? |? ? ? ^? Session 2 (A-S2)? ^
? |? 18.181.0.31:1235? |? ? ? |? ? ? |? 138.76.29.7:1235? |
? v 155.99.25.11:62000 v? ? ? |? ? ? v 155.99.25.11:62000 v
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? ? ? ? ? ? ? ? ? ? ? Cone NAT
? ? ? ? ? ? ? ? ? ? ? ? 155.99.25.11
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ^? Session 1 (A-S1)? ^? ? ? |? ? ? ^? Session 2 (A-S2)? ^
? |? 18.181.0.31:1235? |? ? ? |? ? ? |? 138.76.29.7:1235? |
? v? 10.0.0.1:1234? ? v? ? ? |? ? ? v? 10.0.0.1:1234? ? v
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? ? ? ? ? ? ? ? ? ? ? Client A
? ? ? ? ? ? ? ? ? ? ? ? 10.0.0.1:1234
其中Cone NAT根據(jù)NAT如何接收已經(jīng)建立的(公網(wǎng)IP,公網(wǎng)端口)對(duì)的輸入數(shù)據(jù)還可以細(xì)分為以下三類:
1) 全錐形NAT(Full Cone NAT) 在一個(gè)新會(huì)話建立了公網(wǎng)/內(nèi)網(wǎng)端口綁定之后,全錐形NAT接下來會(huì)接受對(duì)應(yīng)公網(wǎng)端口的所有數(shù)據(jù),無論是來自哪個(gè)(公網(wǎng))終端。 全錐NAT有時(shí)候也被稱為“混雜”NAT(promiscuous NAT)。
2) 受限錐形NAT(Restricted Cone NAT) 受限錐形NAT只會(huì)轉(zhuǎn)發(fā)符合某個(gè)條件的輸入數(shù)據(jù)包。條件為:外部(源)IP地址匹配內(nèi)網(wǎng)主機(jī)之前發(fā)送一個(gè)或多個(gè)數(shù)據(jù)包的結(jié)點(diǎn)的IP地址。 AT通過限制輸入數(shù)據(jù)包為一組“已知的”外部IP地址,有效地精簡(jiǎn)了防火墻的規(guī)則。
3) 端口受限錐形NAT(Port-Restricted Cone NAT) 端口受限錐形NAT也類似,只當(dāng)外部數(shù)據(jù)包的IP地址和端口號(hào)都匹配內(nèi)網(wǎng)主機(jī)發(fā)送過的地址和端口號(hào)時(shí)才進(jìn)行轉(zhuǎn)發(fā)。 端口受限錐形NAT為內(nèi)部結(jié)點(diǎn)提供了和對(duì)稱NAT相同等級(jí)的保護(hù),以隔離未關(guān)聯(lián)的數(shù)據(jù)。
對(duì)稱NAT(Symmetric NAT): 對(duì)稱NAT正好相反,不在所有公網(wǎng)-內(nèi)網(wǎng)對(duì)的會(huì)話中維持一個(gè)固定的端口綁定。其為每個(gè)新的會(huì)話開辟一個(gè)新的端口。如下圖所示:
? Server S1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Server S2
18.181.0.31:1235? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 138.76.29.7:1235
? ? ? |? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? |? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? +----------------------+----------------------+
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ^? Session 1 (A-S1)? ^? ? ? |? ? ? ^? Session 2 (A-S2)? ^
? |? 18.181.0.31:1235? |? ? ? |? ? ? |? 138.76.29.7:1235? |
? v 155.99.25.11:62000 v? ? ? |? ? ? v 155.99.25.11:62001 v
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? ? ? ? ? ? ? ? ? ? Symmetric NAT
? ? ? ? ? ? ? ? ? ? ? ? 155.99.25.11
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ^? Session 1 (A-S1)? ^? ? ? |? ? ? ^? Session 2 (A-S2)? ^
? |? 18.181.0.31:1235? |? ? ? |? ? ? |? 138.76.29.7:1235? |
? v? 10.0.0.1:1234? ? v? ? ? |? ? ? v? 10.0.0.1:1234? ? v
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? ? ? ? ? ? ? ? ? ? ? Client A
? ? ? ? ? ? ? ? ? ? ? ? 10.0.0.1:1234
2. P2P通信
根據(jù)客戶端的不同,客戶端之間進(jìn)行P2P傳輸?shù)姆椒ㄒ猜杂胁煌?,這里介紹了現(xiàn)有的穿越中間件進(jìn)行P2P通信的幾種技術(shù)。
2.1 中繼(Relaying)
這是最可靠但也是最低效的一種P2P通信實(shí)現(xiàn)。其原理是通過一個(gè)有公網(wǎng)IP的服務(wù)器中間人對(duì)兩個(gè)內(nèi)網(wǎng)客戶端的通信數(shù)據(jù)進(jìn)行中繼和轉(zhuǎn)發(fā)。如下圖所示:
? ? ? ? ? ? ? ? ? ? ? Server S
? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? ? ? ? ? ? ? ? ? ? ? |
? +----------------------+----------------------+
? |? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
NAT A? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? NAT B
? |? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? |? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
Client A? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Client B
客戶端A和客戶端B不直接通信,而是先都與服務(wù)端S建立鏈接,然后再通過S和對(duì)方建立的通路來中繼傳遞的數(shù)據(jù)。這鐘方法的缺陷很明顯, 當(dāng)鏈接的客戶端變多之后,會(huì)顯著增加服務(wù)器的負(fù)擔(dān),完全沒體現(xiàn)出P2P的優(yōu)勢(shì)。但這種方法的好處是能保證成功,因此在實(shí)踐中也常作為一種備選方案。
2.2 逆向鏈接(Connection reversal)
第二種方法在當(dāng)兩個(gè)端點(diǎn)中有一個(gè)不存在中間件的時(shí)候有效。例如,客戶端A在NAT之后而客戶端B擁有全局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
客戶端A內(nèi)網(wǎng)地址為10.0.0.1,且應(yīng)用程序正在使用TCP端口1234。A和服務(wù)器S建立了一個(gè)鏈接,服務(wù)器的IP地址為18.181.0.31,監(jiān)聽1235端口。NAT A給客戶端A分配了TCP端口62000,地址為NAT的公網(wǎng)IP地址155.99.25.11, 作為客戶端A對(duì)外當(dāng)前會(huì)話的臨時(shí)IP和端口。因此S認(rèn)為客戶端A就是155.99.25.11:62000。而B由于有公網(wǎng)地址,所以對(duì)S來說B就是138.76.29.7:1234。
當(dāng)客戶端B想要發(fā)起一個(gè)對(duì)客戶端A的P2P鏈接時(shí),要么鏈接A的外網(wǎng)地址155.99.25.11:62000,要么鏈接A的內(nèi)網(wǎng)地址10.0.0.1:1234,然而兩種方式鏈接都會(huì)失敗。 鏈接10.0.0.1:1234失敗自不用說,為什么鏈接155.99.25.11:62000也會(huì)失敗呢?來自B的TCP SYN握手請(qǐng)求到達(dá)NAT A的時(shí)候會(huì)被拒絕,因?yàn)閷?duì)NAT A來說只有外出的鏈接才是允許的。 在直接鏈接A失敗之后,B可以通過S向A中繼一個(gè)鏈接請(qǐng)求,從而從A方向“逆向“地建立起A-B之間的點(diǎn)對(duì)點(diǎn)鏈接。
很多當(dāng)前的P2P系統(tǒng)都實(shí)現(xiàn)了這種技術(shù),但其局限性也是很明顯的,只有當(dāng)其中一方有公網(wǎng)IP時(shí)鏈接才能建立。越來越多的情況下, 通信的雙方都在NAT之后,因此就要用到我們下面介紹的第三種技術(shù)了。
2.3 UDP打洞(UDP hole punching)
第三種P2P通信技術(shù),被廣泛采用的,名為“P2P打洞“。P2P打洞技術(shù)依賴于通常防火墻和cone NAT允許正當(dāng)?shù)腜2P應(yīng)用程序在中間件中打洞且與對(duì)方建立直接鏈接的特性。 以下主要考慮兩種常見的場(chǎng)景,以及應(yīng)用程序如何設(shè)計(jì)去完美地處理這些情況。第一種場(chǎng)景代表了大多數(shù)情況,即兩個(gè)需要直接鏈接的客戶端處在兩個(gè)不同的NAT 之后;第二種場(chǎng)景是兩個(gè)客戶端在同一個(gè)NAT之后,但客戶端自己并不需要知道。
2.3.1. 端點(diǎn)在不同的NAT之后
假設(shè)客戶端A和客戶端B的地址都是內(nèi)網(wǎng)地址,且在不同的NAT后面。A、B上運(yùn)行的P2P應(yīng)用程序和服務(wù)器S都使用了UDP端口1234,A和B分別初始化了 與Server的UDP通信,地址映射如圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? 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
現(xiàn)在假設(shè)客戶端A打算與客戶端B直接建立一個(gè)UDP通信會(huì)話。如果A直接給B的公網(wǎng)地址138.76.29.7:31000發(fā)送UDP數(shù)據(jù),NAT B將很可能會(huì)無視進(jìn)入的 數(shù)據(jù)(除非是Full Cone NAT),因?yàn)樵吹刂泛投丝谂cS不匹配,而最初只與S建立過會(huì)話。B往A直接發(fā)信息也類似。
假設(shè)A開始給B的公網(wǎng)地址發(fā)送UDP數(shù)據(jù)的同時(shí),給服務(wù)器S發(fā)送一個(gè)中繼請(qǐng)求,要求B開始給A的公網(wǎng)地址發(fā)送UDP信息。A往B的輸出信息會(huì)導(dǎo)致NAT A打開 一個(gè)A的內(nèi)網(wǎng)地址與與B的外網(wǎng)地址之間的新通訊會(huì)話,B往A亦然。一旦新的UDP會(huì)話在兩個(gè)方向都打開之后,客戶端A和客戶端B就能直接通訊, 而無須再通過引導(dǎo)服務(wù)器S了。
UDP打洞技術(shù)有許多有用的性質(zhì)。一旦一個(gè)的P2P鏈接建立,鏈接的雙方都能反過來作為“引導(dǎo)服務(wù)器”來幫助其他中間件后的客戶端進(jìn)行打洞, 極大減少了服務(wù)器的負(fù)載。應(yīng)用程序不需要知道中間件具體是什么(如果有的話),因?yàn)橐陨系倪^程在沒有中間件或者有多個(gè)中間件的情況下 也一樣能建立通信鏈路。
2.3.2. 端點(diǎn)在相同的NAT之后
現(xiàn)在考慮這樣一種情景,兩個(gè)客戶端A和B正好在同一個(gè)NAT之后(而且可能他們自己并不知道),因此在同一個(gè)內(nèi)網(wǎng)網(wǎng)段之內(nèi)。 客戶端A和服務(wù)器S建立了一個(gè)UDP會(huì)話,NAT為此分配了公網(wǎng)端口62000,B同樣和S建立會(huì)話,分配到了端口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
假設(shè)A和B使用了上節(jié)介紹的UDP打洞技術(shù)來建立P2P通路,那么會(huì)發(fā)生什么呢?首先A和B會(huì)得到由S觀測(cè)到的對(duì)方的公網(wǎng)IP和端口號(hào),然后給對(duì)方的地址發(fā)送信息。 兩個(gè)客戶端只有在NAT允許內(nèi)網(wǎng)主機(jī)對(duì)內(nèi)網(wǎng)其他主機(jī)發(fā)起UDP會(huì)話的時(shí)候才能正常通信,我們把這種情況稱之為”回環(huán)傳輸“(lookback translation),因?yàn)閺膬?nèi)部 到達(dá)NAT的數(shù)據(jù)會(huì)被“回送”到內(nèi)網(wǎng)中而不是轉(zhuǎn)發(fā)到外網(wǎng)。例如,當(dāng)A發(fā)送一個(gè)UDP數(shù)據(jù)包給B的公網(wǎng)地址時(shí),數(shù)據(jù)包最初有源IP地址和端口地址10.0.0.1:1234和 目的地址155.99.25.11:62001,NAT收到包后,將其轉(zhuǎn)換為源155.99.25.11:62000(A的公網(wǎng)地址)和目的10.1.1.3:1234,然后再轉(zhuǎn)發(fā)給B。即便NAT支持 回環(huán)傳輸,這種轉(zhuǎn)換和轉(zhuǎn)發(fā)在此情況下也是沒必要的,且有可能會(huì)增加A與B的對(duì)話延時(shí)和加重NAT的負(fù)擔(dān)。
對(duì)于這個(gè)情況,優(yōu)化方案是很直觀的。當(dāng)A和B最初通過S交換地址信息時(shí),他們應(yīng)該包含自身的IP地址和端口號(hào)(從自己看),同時(shí)也包含從服務(wù)器看的自己的 地址和端口號(hào)。然后客戶端同時(shí)開始從對(duì)方已知的兩個(gè)的地址中同時(shí)開始互相發(fā)送數(shù)據(jù),并使用第一個(gè)成功通信的地址作為對(duì)方地址。如果兩個(gè)客戶端在同一個(gè) NAT后,發(fā)送到對(duì)方內(nèi)網(wǎng)地址的數(shù)據(jù)最有可能先到達(dá),從而可以建立一條不經(jīng)過NAT的通信鏈路;如果兩個(gè)客戶端在不同的NAT之后,發(fā)送給對(duì)方內(nèi)網(wǎng)地址的數(shù)據(jù)包 根本就到達(dá)不了對(duì)方,但仍然可以通過公網(wǎng)地址來建立通路。值得一提的是,雖然這些數(shù)據(jù)包通過某種方式驗(yàn)證,但是在不同NAT的情況下完全有可能會(huì)導(dǎo)致A往B 發(fā)送的信息發(fā)送到其他A內(nèi)網(wǎng)網(wǎng)段中無關(guān)的結(jié)點(diǎn)上去的。
2.3.3. 端點(diǎn)在多級(jí)NAT之后
在一些拓樸結(jié)構(gòu)中,可能會(huì)存在多級(jí)NAT設(shè)備,在這種情況下,如果沒有關(guān)于拓樸的具體信息, 兩個(gè)Peer要建立“最優(yōu)”的P2P鏈接是不可能的,下面來說為什么。以下圖為例:
? ? ? ? ? ? ? ? ? ? ? ? ? ? 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
假設(shè)NAT X是一個(gè)網(wǎng)絡(luò)提供商ISP部署的工業(yè)級(jí)NAT,其下子網(wǎng)共用一個(gè)公網(wǎng)地址155.99.25.11,NAT A和NAT B分別是其下不同用戶的網(wǎng)關(guān)部署的NAT。只有服務(wù)器S 和NAT X有全局的路由地址。Client A在NAT A的子網(wǎng)中,同時(shí)Client B在NAT B的子網(wǎng)中,每經(jīng)過一級(jí)NAT都要進(jìn)行一次網(wǎng)絡(luò)地址轉(zhuǎn)換。
現(xiàn)在假設(shè)A和B打算建立直接P2P鏈接,用一般的方法(通過Server S來打洞)自然是沒問題的,那能不能優(yōu)化呢?一種想當(dāng)然的優(yōu)化辦法是A直接把信息發(fā)送給NAT B的 內(nèi)網(wǎng)地址192.168.1.2:31000,且B通過NAT B把信息發(fā)送給A的路由地址192.168.1.1:30000,不幸的是,A和B都沒有辦法得知這兩個(gè)目的地址,因?yàn)镾只看見了客戶端 ‵全局‵地址155.99.25.11。退一步說,即便A和B通過某種方法得知了那些地址,我們也無法保證他們是可用的。因?yàn)镮SP分配的子網(wǎng)地址可能和NAT A B分配的子網(wǎng)地址 域相沖突。因此客戶端沒有其他選擇,只能使用S來進(jìn)行打洞并進(jìn)行回環(huán)傳輸。
2.3.4. 固定端口綁定
UDP打洞技術(shù)有一個(gè)主要的條件:只有當(dāng)兩個(gè)NAT都是Cone NAT(或者非NAT的防火墻)時(shí)才能工作。因?yàn)槠渚S持了一個(gè)給定的(內(nèi)網(wǎng)IP,內(nèi)網(wǎng)UDP)二元組 和(公網(wǎng)IP, 公網(wǎng)UDP)二元組固定的端口綁定,只要該UDP端口還在使用中,就不會(huì)變化。如果像對(duì)稱NAT一樣,給每個(gè)新會(huì)話分配一個(gè)新的公網(wǎng)端口,就 會(huì)導(dǎo)致UDP應(yīng)用程序無法使用跟外部端點(diǎn)已經(jīng)打通了的通信鏈路。由于Cone NAT是當(dāng)今最廣泛使用的,盡管有一小部分的對(duì)稱NAT是不支持打洞的,UDP打洞 技術(shù)也還是被廣泛采納應(yīng)用。
3.具體實(shí)現(xiàn)
一般的網(wǎng)絡(luò)編程,都是客戶端比服務(wù)端要難,因?yàn)橐幚砼c服務(wù)器的通信同時(shí)還要處理來自用戶的事件;對(duì)于P2P客戶端來說更是如此,因?yàn)镻2P客戶端不止作 為客戶端,同時(shí)也作為對(duì)等連接的服務(wù)器端。這里的大體思路是,輸入命令傳輸給服務(wù)器之后,接收來自服務(wù)器的反饋,并執(zhí)行相應(yīng)代碼。例如A想要與B建立 通信鏈路,先給服務(wù)器發(fā)送punch命令以及給B發(fā)送數(shù)據(jù),服務(wù)器接到命令后給B發(fā)送punch_requst信息以及A的端點(diǎn)信息,B收到之后向A發(fā)送數(shù)據(jù)打通通路,然 后A與B就可以進(jìn)行P2P通信了。經(jīng)測(cè)試,打通通路后即便把服務(wù)器關(guān)閉,A與B也能正常通信。
一個(gè)UDP打洞的例子見P2P-Over-MiddleBoxes-Demo
UPDATE 2016-04-06
關(guān)于TCP打洞,有一點(diǎn)需要提的是,因?yàn)門CP是基于連接的,所以任何未經(jīng)連接而發(fā)送的數(shù)據(jù)都會(huì)被丟棄,這導(dǎo)致在recv的時(shí)候是無法直接從peer端讀取數(shù)據(jù)。 其實(shí)這對(duì)UDP也一樣,如果對(duì)UDP的socket進(jìn)行了connect,其也會(huì)忽略連接之外的數(shù)據(jù),詳見connect(2)。
所以,如果我們要進(jìn)行TCP打洞,通常需要重用本地的endpoint來發(fā)起新的TCP連接,這樣才能將已經(jīng)打開的NAT利用起來。具體來說,則是要設(shè)置socket的 SO_REUSEADDR或SO_REUSEPORT屬性,根據(jù)系統(tǒng)不同,其實(shí)現(xiàn)也不盡一致。一般來說,TCP打洞的步驟如下:
A 發(fā)送 SYN 到 B (出口地址,下同),從而創(chuàng)建NAT A的一組映射
B 發(fā)送 SYN 到 A, 創(chuàng)建NAT B的一組映射
根據(jù)時(shí)序不同,兩個(gè)SYN中有一個(gè)會(huì)被對(duì)方的NAT丟棄,另一個(gè)成功通過NAT
通過NAT的SYN報(bào)文被其中一方收到,即返回SYNACK, 完成握手
至此,TCP的打洞成功,獲得一個(gè)不依賴于服務(wù)器的鏈接