1、內(nèi)容概述
P2P即點(diǎn)對(duì)點(diǎn)通信,或稱為對(duì)等聯(lián)網(wǎng),與傳統(tǒng)的服務(wù)器客戶端模式(如下圖“P2P結(jié)構(gòu)模型”所示)有著明顯的區(qū)別,在即時(shí)通訊方案中應(yīng)用廣泛(比如IM應(yīng)用中的實(shí)時(shí)音視頻通信、實(shí)時(shí)文件傳輸甚至文字聊天等)。
P2P可以是一種通信模式、一種邏輯網(wǎng)絡(luò)模型、一種技術(shù)、甚至一種理念。在P2P網(wǎng)絡(luò)中(如右圖所示),所有通信節(jié)點(diǎn)的地位都是對(duì)等的,每個(gè)節(jié)點(diǎn)都扮演著客戶機(jī)和服務(wù)器雙重角色,節(jié)點(diǎn)之間通過(guò)直接通信實(shí)現(xiàn)文件信息、處理器運(yùn)算能力、存儲(chǔ)空間等資源的共享。P2P網(wǎng)絡(luò)具有分散性、可擴(kuò)展性、健壯性等特點(diǎn),這使得P2P技術(shù)在信息共享、即時(shí)通訊、協(xié)同工作、分布式計(jì)算、網(wǎng)絡(luò)存儲(chǔ)等領(lǐng)域都有廣闊的應(yīng)用。
圖1 - 經(jīng)典的CS模式:
圖2 - P2P結(jié)構(gòu)模型:
NAT技術(shù)和P2P技術(shù)作為經(jīng)典的兩項(xiàng)網(wǎng)絡(luò)技術(shù),在現(xiàn)在的網(wǎng)絡(luò)上有著廣泛的應(yīng)用,P2P主機(jī)位于NAT網(wǎng)關(guān)后面的情況屢見(jiàn)不鮮。NAT技術(shù)雖然在一定程度上解決了IPv4地址短缺的問(wèn)題,在構(gòu)建防火墻、保證網(wǎng)絡(luò)安全方面都發(fā)揮了一定的作用,卻破壞了端到端的網(wǎng)絡(luò)通信。NAT阻礙主機(jī)進(jìn)行P2P通信的主要原因是NAT不允許外網(wǎng)主機(jī)主動(dòng)訪問(wèn)內(nèi)網(wǎng)主機(jī),但是P2P技術(shù)卻要求通信雙方都能主動(dòng)發(fā)起訪問(wèn),所以要在NAT網(wǎng)絡(luò)環(huán)境中進(jìn)行有效的P2P通信,就必須采用新的解決方案。
P2P作為一項(xiàng)實(shí)用的技術(shù),有很大的優(yōu)化空間,并且相對(duì)于網(wǎng)絡(luò)設(shè)備,基于P2P的應(yīng)用程序在實(shí)現(xiàn)上更為靈活。所以為了兼容NAT,基于P2P的應(yīng)用程序在開(kāi)發(fā)的時(shí)候大多會(huì)根據(jù)自身特點(diǎn)加入一些穿越NAT的功能以解決上述問(wèn)題。以下著重介紹幾種常見(jiàn)的P2P穿越NAT方案。
2、反向鏈接技術(shù):一種特殊的P2P場(chǎng)景(通信雙方中只有一方位于NAT設(shè)備之后)
此種情況是所有P2P場(chǎng)景中最簡(jiǎn)單的,它使用一種被稱為“反向鏈接技術(shù)”來(lái)解決這個(gè)問(wèn)題。大致的原理如下所述。
如圖3所示,客戶端A位于NAT之后,它通過(guò)TCP端口1234連接到服務(wù)器的TCP端口1235上,NAT設(shè)備為這個(gè)連接重新分配了TCP端口62000。客戶端B也通過(guò)TCP端口1234連接到服務(wù)器端口1235上。A和B從服務(wù)器處獲知的對(duì)方的外網(wǎng)地址二元組{IP地址:端口號(hào)}分別為{138.76.29.7:1234}和{155.99.25.11:62000},它們?cè)诟髯缘谋镜囟丝谏线M(jìn)行偵聽(tīng)。
由于B 擁有外網(wǎng)IP地址,所以A要發(fā)起與B的通信,可以直接通過(guò)TCP連接到B。但如果B嘗試通過(guò)TCP連接到A進(jìn)行P2P通信,則會(huì)失敗,原因是A位于NAT設(shè)備后,雖然B發(fā)出的TCP SYN請(qǐng)求能夠到達(dá)NAT設(shè)備的端口62000,但NAT設(shè)備會(huì)拒絕這個(gè)連接請(qǐng)求。要想與Client A通信, B不是直接向A發(fā)起連接,而是通過(guò)服務(wù)器給A轉(zhuǎn)發(fā)一個(gè)連接請(qǐng)求,反過(guò)來(lái)請(qǐng)求A連接到B(即進(jìn)行反向鏈接),A在收到從服務(wù)器轉(zhuǎn)發(fā)過(guò)來(lái)的請(qǐng)求以后,會(huì)主動(dòng)向B發(fā)起一個(gè)TCP的連接請(qǐng)求,這樣在NAT設(shè)備上就會(huì)建立起關(guān)于這個(gè)連接的相關(guān)表項(xiàng),使A和B之間能夠正常通信,從而建立起它們之間的TCP連接。
圖3 - 反向鏈接示意圖:
3、基于UDP協(xié)議的P2P打洞技術(shù)詳解
1原理概述
UDP打洞技術(shù)是通過(guò)中間服務(wù)器的協(xié)助在各自的NAT網(wǎng)關(guān)上建立相關(guān)的表項(xiàng),使P2P連接的雙方發(fā)送的報(bào)文能夠直接穿透對(duì)方的NAT網(wǎng)關(guān),從而實(shí)現(xiàn)P2P客戶端互連。如果兩臺(tái)位于NAT設(shè)備后面的P2P客戶端希望在自己的NAT網(wǎng)關(guān)上打個(gè)洞,那么他們需要一個(gè)協(xié)助者——集中服務(wù)器,并且還需要一種用于打洞的Session建立機(jī)制。
什么是集中服務(wù)器?
集中服務(wù)器本質(zhì)上是一臺(tái)被設(shè)置在公網(wǎng)上的服務(wù)器,建立P2P的雙方都可以直接訪問(wèn)到這臺(tái)服務(wù)器。位于NAT網(wǎng)關(guān)后面的客戶端A和B都可以與一臺(tái)已知的集中服務(wù)器建立連接,并通過(guò)這臺(tái)集中服務(wù)器了解對(duì)方的信息并中轉(zhuǎn)各自的信息。
同時(shí)集中服務(wù)器的另一個(gè)重要作用在于判斷某個(gè)客戶端是否在NAT網(wǎng)關(guān)之后。具體的方法是:一個(gè)客戶端在集中服務(wù)器上登陸的時(shí)候,服務(wù)器記錄下該客戶端的兩對(duì)地址二元組信息{IP地址:UDP端口},一對(duì)是該客戶端與集中服務(wù)器進(jìn)行通信的自身的IP地址和端口號(hào),另一對(duì)是集中服務(wù)器記錄下的由服務(wù)器“觀察”到的該客戶端實(shí)際與自己通信所使用的IP地址和端口號(hào)。我們可以把前一對(duì)地址二元組看作是客戶端的內(nèi)網(wǎng)IP地址和端口號(hào),把后一對(duì)地址二元組看作是客戶端的內(nèi)網(wǎng)IP地址和端口號(hào)經(jīng)過(guò)NAT轉(zhuǎn)換后的外網(wǎng)IP地址和端口號(hào)。集中服務(wù)器可以從客戶端的登陸消息中得到該客戶端的內(nèi)網(wǎng)相關(guān)信息,還可以通過(guò)登陸消息的IP頭和UDP頭得到該客戶端的外網(wǎng)相關(guān)信息。如果該客戶端不是位于NAT設(shè)備后面,那么采用上述方法得到的兩對(duì)地址二元組信息是完全相同的。
P2P的Session建立原理:
假定客戶端A要發(fā)起對(duì)客戶端B的直接連接,具體的“打洞”過(guò)程如下:
- 1)A最初不知道如何向客戶端B發(fā)起連接,于是A向集中服務(wù)器發(fā)送消息,請(qǐng)求集中服務(wù)器幫助建立與客戶端B的UDP連接。
- 2)集中服務(wù)器將含有B的外網(wǎng)和內(nèi)網(wǎng)的地址二元組發(fā)給A,同時(shí),集中服務(wù)器將包含有A的外網(wǎng)和內(nèi)網(wǎng)的地址二元組信息的消息也發(fā)給B。這樣一來(lái), A與B就都知道對(duì)方外網(wǎng)和內(nèi)網(wǎng)的地址二元組信息了。
- 3)當(dāng)A收到由集中服務(wù)器發(fā)來(lái)的包含B的外網(wǎng)和內(nèi)網(wǎng)的地址二元組信息后, A開(kāi)始向B的地址二元組發(fā)送UDP數(shù)據(jù)包,并且A會(huì)自動(dòng)鎖定第一個(gè)給出響應(yīng)的B的地址二元組。同理,當(dāng)B收到由集中服務(wù)器發(fā)來(lái)的A的外網(wǎng)和內(nèi)網(wǎng)地址二元組信息后,也會(huì)開(kāi)始向A的外網(wǎng)和內(nèi)網(wǎng)的地址二元組發(fā)送UDP數(shù)據(jù)包,并且自動(dòng)鎖定第一個(gè)得到A回應(yīng)的地址二元組。由于A與B互相向?qū)Ψ桨l(fā)送UDP數(shù)據(jù)包的操作是異步的,所以A和B發(fā)送數(shù)據(jù)包的時(shí)間先后并沒(méi)有時(shí)序要求。
下面來(lái)看下這三者之間是如何進(jìn)行UDP打洞的。在這我們分三種具體情景來(lái)討論:
- 第一種是最簡(jiǎn)單的一種情景,兩個(gè)客戶端都位于同一個(gè)NAT設(shè)備后面,即位于同一內(nèi)網(wǎng)中;
- 第二種是最普遍的一種情景,兩個(gè)客戶端分別位于不同的NAT設(shè)備后面,分屬不同的內(nèi)網(wǎng);
- 第三種是客戶端位于兩層NAT設(shè)備之后,通常最上層的NAT是由網(wǎng)絡(luò)提供商提供的,第二層NAT是家用的NAT路由器之類的設(shè)備提供的。
2 典型P2P情景1:兩客戶端位于同一NAT設(shè)備后面(即相同內(nèi)網(wǎng)中)
這是最簡(jiǎn)單的一種情況(如圖4所示):客戶端A和B分別與集中服務(wù)器建立UDP連接,經(jīng)過(guò)NAT轉(zhuǎn)換后,A的公網(wǎng)端口被映射為62000,B的公網(wǎng)端口映射為62005。
圖4 - 位于同一個(gè)NAT設(shè)備后的UDP打洞過(guò)程
當(dāng)A向集中服務(wù)器發(fā)出消息請(qǐng)求與B進(jìn)行連接,集中服務(wù)器將B的外網(wǎng)地址二元組以及內(nèi)網(wǎng)地址二元組發(fā)給A,同時(shí)把A的外網(wǎng)以及內(nèi)網(wǎng)的地址二元組信息發(fā)給B。A和B發(fā)往對(duì)方公網(wǎng)地址二元組信息的UDP數(shù)據(jù)包不一定會(huì)被對(duì)方收到,這取決于當(dāng)前的NAT設(shè)備是否支持不同端口之間的UDP數(shù)據(jù)包能否到達(dá)(即Hairpin轉(zhuǎn)換特性),無(wú)論如何A與B發(fā)往對(duì)方內(nèi)網(wǎng)的地址二元組信息的UDP數(shù)據(jù)包是一定可以到達(dá)的,內(nèi)網(wǎng)數(shù)據(jù)包不需要路由,且速度更快。A與B推薦采用內(nèi)網(wǎng)的地址二元組信息進(jìn)行常規(guī)的P2P通信。
假定NAT設(shè)備支持Hairpin轉(zhuǎn)換,P2P雙方也應(yīng)忽略與內(nèi)網(wǎng)地址二元組的連接,如果A 和B采用外網(wǎng)的地址二元組做為P2P通信的連接,這勢(shì)必會(huì)造成數(shù)據(jù)包無(wú)謂地經(jīng)過(guò)NAT設(shè)備,這是一種對(duì)資源的浪費(fèi)。就目前的網(wǎng)絡(luò)情況而言,應(yīng)用程序在“打洞”的時(shí)候,最好還是把外網(wǎng)和內(nèi)網(wǎng)的地址二元組都嘗試一下。如果都能成功,優(yōu)先以內(nèi)網(wǎng)地址進(jìn)行連接。
什么是Hairpin技術(shù)?
Hairpin技術(shù)又被稱為Hairpin NAT、Loopback NAT或Hairpin Translation。Hairpin技術(shù)需要NAT網(wǎng)關(guān)支持,它能夠讓兩臺(tái)位于同一臺(tái)NAT網(wǎng)關(guān)后面的主機(jī),通過(guò)對(duì)方的公網(wǎng)地址和端口相互訪問(wèn),NAT網(wǎng)關(guān)會(huì)根據(jù)一系列規(guī)則,將對(duì)內(nèi)部主機(jī)發(fā)往其NAT公網(wǎng)IP地址的報(bào)文進(jìn)行轉(zhuǎn)換,并從私網(wǎng)接口發(fā)送給目標(biāo)主機(jī)。目前有很多NAT設(shè)備不支持該技術(shù),這種情況下,NAT網(wǎng)關(guān)在一些特定場(chǎng)合下將會(huì)阻斷P2P穿越NAT的行為,打洞的嘗試是無(wú)法成功的。好在現(xiàn)在已經(jīng)有越來(lái)越多的NAT設(shè)備商開(kāi)始加入到對(duì)該轉(zhuǎn)換的支持中來(lái)。
3 典型P2P情景2:兩客戶端位于不同的NAT設(shè)備后面(分屬不同的內(nèi)網(wǎng))
這是最普遍的一種情況(如圖5所示):客戶端A與B經(jīng)由各自的NAT設(shè)備與集中服務(wù)器建立UDP連接, A與B的本地端口號(hào)均為4321,集中服務(wù)器的公網(wǎng)端口號(hào)為1234。在向外的會(huì)話中, A的外網(wǎng)IP被映射為155.99.25.11,外網(wǎng)端口為62000;B的外網(wǎng)IP被映射為138.76.29.7,外網(wǎng)端口為31000。
如下所示:
客戶端A——>本地IP:10.0.0.1,本地端口:4321,外網(wǎng)IP:155.99.25.11,外網(wǎng)端口:62000
客戶端B——>本地IP:10.1.1.3,本地端口:4321,外網(wǎng)IP:138.76.29.7,外網(wǎng)端口:31000
圖5 - 位于不同NAT設(shè)備后的UDP打洞過(guò)程
在A向服務(wù)器發(fā)送的登陸消息中,包含有A的內(nèi)網(wǎng)地址二元組信息,即10.0.0.1:4321;服務(wù)器會(huì)記錄下A的內(nèi)網(wǎng)地址二元組信息,同時(shí)會(huì)把自己觀察到的A的外網(wǎng)地址二元組信息記錄下來(lái)。同理,服務(wù)器也會(huì)記錄下B的內(nèi)網(wǎng)地址二元組信息和由服務(wù)器觀察到的客戶端B的外網(wǎng)地址二元組信息。無(wú)論A與B二者中的任何一方向服務(wù)器發(fā)送P2P連接請(qǐng)求,服務(wù)器都會(huì)將其記錄下來(lái)的上述的外網(wǎng)和內(nèi)網(wǎng)地址二元組發(fā)送給A或B。
A和B分屬不同的內(nèi)網(wǎng),它們的內(nèi)網(wǎng)地址在外網(wǎng)中是沒(méi)有路由的,所以發(fā)往各自內(nèi)網(wǎng)地址的UDP數(shù)據(jù)包會(huì)發(fā)送到錯(cuò)誤的主機(jī)或者根本不存在的主機(jī)上。當(dāng)A的第一個(gè)消息發(fā)往B的外網(wǎng)地址(如圖3所示),該消息途經(jīng)A的NAT設(shè)備,并在該設(shè)備上生成一個(gè)會(huì)話表項(xiàng),該會(huì)話的源地址二元組信息是{10.0.0.1:4321},和A與服務(wù)器建立連接的時(shí)候NAT生成的源地址二元組信息一樣,但它的目的地址是B的外網(wǎng)地址。在A的NAT設(shè)備支持保留A的內(nèi)網(wǎng)地址二元組信息的情況下,所有來(lái)自A的源地址二元組信息為{10.0.0.1:4321}的數(shù)據(jù)包都沿用A與集中服務(wù)器事先建立起來(lái)的會(huì)話,這些數(shù)據(jù)包的外網(wǎng)地址二元組信息均被映射為{155.99.25.11:62000}。
A向B的外網(wǎng)地址發(fā)送消息的過(guò)程就是“打洞”的過(guò)程,從A的內(nèi)網(wǎng)的角度來(lái)看應(yīng)為從{10.0.0.1:4321}發(fā)往{138.76.29.7:31000},從A在其NAT設(shè)備上建立的會(huì)話來(lái)看,是從{155.99.25.11:62000}發(fā)到{138.76.29.7:31000}。如果A發(fā)給B的外網(wǎng)地址二元組的消息包在B向A發(fā)送消息包之前到達(dá)B的NAT設(shè)備,B的NAT設(shè)備會(huì)認(rèn)為A發(fā)過(guò)來(lái)的消息是未經(jīng)授權(quán)的外網(wǎng)消息,并丟棄該數(shù)據(jù)包。
B發(fā)往A的消息包也會(huì)在B的NAT設(shè)備上建立一個(gè){10.1.1.3:4321,155.99.25.11:62000}的會(huì)話(通常也會(huì)沿用B與集中服務(wù)器連接時(shí)建立的會(huì)話,只是該會(huì)話現(xiàn)在不僅接受由服務(wù)器發(fā)給B的消息,還可以接受從A的NAT設(shè)備{155.99.25.11:6200}發(fā)來(lái)的消息)。
一旦A與B都向?qū)Ψ降腘AT設(shè)備在外網(wǎng)上的地址二元組發(fā)送了數(shù)據(jù)包,就打開(kāi)了A與B之間的“洞”,A與B向?qū)Ψ降耐饩W(wǎng)地址發(fā)送數(shù)據(jù),等效為向?qū)Ψ降目蛻舳酥苯影l(fā)送UDP數(shù)據(jù)包了。一旦應(yīng)用程序確認(rèn)已經(jīng)可以通過(guò)往對(duì)方的外網(wǎng)地址發(fā)送數(shù)據(jù)包的方式讓數(shù)據(jù)包到達(dá)NAT后面的目的應(yīng)用程序,程序會(huì)自動(dòng)停止繼續(xù)發(fā)送用于“打洞”的數(shù)據(jù)包,轉(zhuǎn)而開(kāi)始真正的P2P數(shù)據(jù)傳輸。
4 典型P2P情景3:兩客戶端位于兩層(或多層)NAT設(shè)備之后(分屬不同的內(nèi)網(wǎng))
此種情景最典型的部署情況就像這樣:最上層的NAT設(shè)備通常是由網(wǎng)絡(luò)提供商(ISP)提供,下層NAT設(shè)備是家用路由器。
如圖6所示:假定NAT C是由ISP提供的NAT設(shè)備,NAT C提供將多個(gè)用戶節(jié)點(diǎn)映射到有限的幾個(gè)公網(wǎng)IP的服務(wù),NAT A和NAT B作為NAT C的內(nèi)網(wǎng)節(jié)點(diǎn)將把用戶的內(nèi)部網(wǎng)絡(luò)接入NAT C的內(nèi)網(wǎng),用戶的內(nèi)部網(wǎng)絡(luò)就可以經(jīng)由NAT C訪問(wèn)公網(wǎng)了。從這種拓?fù)浣Y(jié)構(gòu)上來(lái)看,只有服務(wù)器與NAT C是真正擁有公網(wǎng)可路由IP地址的設(shè)備,而NAT A和NAT B所使用的公網(wǎng)IP地址,實(shí)際上是由ISP服務(wù)提供商設(shè)定的(相對(duì)于NAT C而言)內(nèi)網(wǎng)地址(我們將這種由ISP提供的內(nèi)網(wǎng)地址稱之為“偽”公網(wǎng)地址)。同理,隸屬于NAT A與NAT B的客戶端,它們處于NAT A,NAT B的內(nèi)網(wǎng),以此類推,客戶端可以放到到多層NAT設(shè)備后面。客戶端A和客戶端B發(fā)起對(duì)服務(wù)器S的連接的時(shí)候,就會(huì)依次在NAT A和NAT B上建立向外的Session,而NAT A、NAT B要聯(lián)入公網(wǎng)的時(shí)候,會(huì)在NAT C上再建立向外的Session。
圖6 - 多層NAT下的打洞過(guò)程
現(xiàn)在假定客戶端A和B希望通過(guò)UDP“打洞”完成兩個(gè)客戶端的P2P直連。最優(yōu)化的路由策略是客戶端A向客戶端B的“偽公網(wǎng)”IP上發(fā)送數(shù)據(jù)包,即ISP服務(wù)提供商指定的內(nèi)網(wǎng)IP,NAT B的“偽”公網(wǎng)地址二元組,{10.0.1.2:55000}。由于從服務(wù)器的角度只能觀察到真正的公網(wǎng)地址,也就是NAT A,NAT B在NAT C建立session的真正的公網(wǎng)地址{155.99.25.11:62000}以及{155.99.25.11:62005},非常不幸的是客戶端A與客戶端B是無(wú)法通過(guò)服務(wù)器知道這些“偽”公網(wǎng)的地址,而且即使客戶端A和B通過(guò)某種手段可以得到NAT A和NAT B的“偽”公網(wǎng)地址,我們?nèi)匀徊唤ㄗh采用上述的“最優(yōu)化”的打洞方式,這是因?yàn)檫@些地址是由ISP服務(wù)提供商提供的或許會(huì)存在與客戶端本身所在的內(nèi)網(wǎng)地址重復(fù)的可能性(例如:NAT A的內(nèi)網(wǎng)的IP地址域恰好與NAT A在NAT C的“偽”公網(wǎng)IP地址域重復(fù),這樣就會(huì)導(dǎo)致打洞數(shù)據(jù)包無(wú)法發(fā)出的問(wèn)題)。
因此客戶端別無(wú)選擇,只能使用由公網(wǎng)服務(wù)器觀察到的A,B的公網(wǎng)地址二元組進(jìn)行“打洞”操作,用于“打洞”的數(shù)據(jù)包將由NAT C進(jìn)行轉(zhuǎn)發(fā)。
當(dāng)客戶端A向客戶端B的公網(wǎng)地址二元組{155.99.25.11:62005}發(fā)送UDP數(shù)據(jù)包的時(shí)候,NAT A首先把數(shù)據(jù)包的源地址二元組由A的內(nèi)網(wǎng)地址二元組{10.0.0.1:4321}轉(zhuǎn)換為“偽”公網(wǎng)地址二元組{10.0.1.1:45000},現(xiàn)在數(shù)據(jù)包到了NAT C,NAT C應(yīng)該可以識(shí)別出來(lái)該數(shù)據(jù)包是要發(fā)往自身轉(zhuǎn)換過(guò)的公網(wǎng)地址二元組,如果NAT C可以給出“合理”響應(yīng)的話,NAT C將把該數(shù)據(jù)包的源地址二元組改為{155.99.25.11:62000},目的地址二元組改為{10.0.1.2:55000},即NAT B的“偽”公網(wǎng)地址二元組,NAT B最后會(huì)將收到的數(shù)據(jù)包發(fā)往客戶端B。同樣,由B發(fā)往A的數(shù)據(jù)包也會(huì)經(jīng)過(guò)類似的過(guò)程。目前也有很多NAT設(shè)備不支持類似這樣的“Hairpin轉(zhuǎn)換”,但是已經(jīng)有越來(lái)越多的NAT設(shè)備商開(kāi)始加入對(duì)該轉(zhuǎn)換的支持中來(lái)。
5 一個(gè)需要考慮的現(xiàn)實(shí)問(wèn)題:UDP在空閑狀態(tài)下的超時(shí)
當(dāng)然,從應(yīng)用的角度上來(lái)說(shuō),在完成打洞過(guò)程的同時(shí),還有一些技術(shù)問(wèn)題需要解決,如UDP在空閑狀態(tài)下的超時(shí)問(wèn)題。由于UDP轉(zhuǎn)換協(xié)議提供的“洞”不是絕對(duì)可靠的,多數(shù)NAT設(shè)備內(nèi)部都有一個(gè)UDP轉(zhuǎn)換的空閑狀態(tài)計(jì)時(shí)器,如果在一段時(shí)間內(nèi)沒(méi)有UDP數(shù)據(jù)通信,NAT設(shè)備會(huì)關(guān)掉由“打洞”過(guò)程打出來(lái)的“洞”。如果P2P應(yīng)用程序希望“洞”的存活時(shí)間不受NAT網(wǎng)關(guān)的限制,就最好在穿越NAT以后設(shè)定一個(gè)穿越的有效期。對(duì)于有效期目前沒(méi)有標(biāo)準(zhǔn)值,它與NAT設(shè)備內(nèi)部的配置有關(guān),某些設(shè)備上最短的只有20秒左右。在這個(gè)有效期內(nèi),即使沒(méi)有P2P數(shù)據(jù)包需要傳輸,應(yīng)用程序?yàn)榱司S持該“洞”可以正常工作,也必須向?qū)Ψ桨l(fā)送“打洞”心跳包。這個(gè)心跳包是需要雙方應(yīng)用程序都發(fā)送的,只有一方發(fā)送不會(huì)維持另一方的Session正常工作。除了頻繁發(fā)送“打洞”心跳包以外,還有一個(gè)方法就是在當(dāng)前的“洞”超時(shí)之前,P2P客戶端雙方重新“打洞”,丟棄原有的“洞”,這也不失為一個(gè)有效的方法。
4、基于TCP協(xié)議的P2P打洞技術(shù)詳細(xì)
建立穿越NAT設(shè)備的P2P的TCP連接只比UDP復(fù)雜一點(diǎn)點(diǎn),TCP協(xié)議的”“打洞”從協(xié)議層來(lái)看是與UDP的“打洞”過(guò)程非常相似的。盡管如此,基于TCP協(xié)議的打洞至今為止還沒(méi)有被很好的理解,這也造成了的對(duì)其提供支持的NAT設(shè)備不是很多。在NAT設(shè)備支持的前提下,基于TCP的“打洞”技術(shù)實(shí)際上與基于UDP的“打洞”技術(shù)一樣快捷、可靠。實(shí)際上,只要NAT設(shè)備支持的話,基于TCP的P2P技術(shù)的健壯性將比基于UDP技術(shù)的更強(qiáng)一些,因?yàn)門CP協(xié)議的狀態(tài)機(jī)給出了一種標(biāo)準(zhǔn)的方法來(lái)精確的獲取某個(gè)TCP session的生命期,而UDP協(xié)議則無(wú)法做到這一點(diǎn)。
1 套接字和TCP端口的重用
實(shí)現(xiàn)基于TCP協(xié)議的P2P打洞過(guò)程中,最主要的問(wèn)題不是來(lái)自于TCP協(xié)議,而是來(lái)自于應(yīng)用程序的API接口。這是由于標(biāo)準(zhǔn)的伯克利(Berkeley)套接字的API是圍繞著構(gòu)建客戶端/服務(wù)器程序而設(shè)計(jì)的,API允許TCP流套接字通過(guò)調(diào)用connect()函數(shù)來(lái)建立向外的連接,或者通過(guò)listen()和accept函數(shù)接受來(lái)自外部的連接,但是,API不提供類似UDP那樣的,同一個(gè)端口既可以向外連接,又能夠接受來(lái)自外部的連接。而且更糟的是,TCP的套接字通常僅允許建立1對(duì)1的響應(yīng),即應(yīng)用程序在將一個(gè)套接字綁定到本地的一個(gè)端口以后,任何試圖將第二個(gè)套接字綁定到該端口的操作都會(huì)失敗。
為了讓TCP“打洞”能夠順利工作,我們需要使用一個(gè)本地的TCP端口來(lái)監(jiān)聽(tīng)來(lái)自外部的TCP連接,同時(shí)建立多個(gè)向外的TCP連接。幸運(yùn)的是,所有的主流操作系統(tǒng)都能夠支持特殊的TCP套接字參數(shù),通常叫做“SO_REUSEADDR”,該參數(shù)允許應(yīng)用程序?qū)⒍鄠€(gè)套接字綁定到本地的一個(gè)地址二元組(只要所有要綁定的套接字都設(shè)置了SO_REUSEADDR參數(shù)即可)。BSD系統(tǒng)引入了SO_REUSEPORT參數(shù),該參數(shù)用于區(qū)分端口重用還是地址重用,在這樣的系統(tǒng)里面,上述所有的參數(shù)必須都設(shè)置才行。
2 打開(kāi)P2P的TCP流
假定客戶端A希望建立與B的TCP連接。我們像通常一樣假定A和B已經(jīng)與公網(wǎng)上的已知服務(wù)器建立了TCP連接。服務(wù)器記錄下來(lái)每個(gè)接入的客戶端的公網(wǎng)和內(nèi)網(wǎng)的地址二元組,如同為UDP服務(wù)的時(shí)候一樣。
從協(xié)議層來(lái)看,TCP“打洞”與UDP“打洞”是幾乎完全相同的過(guò)程:
- 客戶端A使用其與服務(wù)器的連接向服務(wù)器發(fā)送請(qǐng)求,要求服務(wù)器協(xié)助其連接客戶端B;
- 服務(wù)器將B的公網(wǎng)和內(nèi)網(wǎng)的TCP地址的二元組信息返回給A,同時(shí),服務(wù)器將A的公網(wǎng)和內(nèi)網(wǎng)的地址二元組也發(fā)送給B;
- 客戶端A和B使用連接服務(wù)器的端口異步地發(fā)起向?qū)Ψ降墓W(wǎng)、內(nèi)網(wǎng)地址二元組的TCP連接,同時(shí)監(jiān)聽(tīng)各自的本地TCP端口是否有外部的連接聯(lián)入;
- A和B開(kāi)始等待向外的連接是否成功,檢查是否有新連接聯(lián)入。如果向外的連接由于某種網(wǎng)絡(luò)錯(cuò)誤而失敗,如:“連接被重置”或者“節(jié)點(diǎn)無(wú)法訪問(wèn)”,客戶端只需要延遲一小段時(shí)間(例如延遲一秒鐘),然后重新發(fā)起連接即可,延遲的時(shí)間和重復(fù)連接的次數(shù)可以由應(yīng)用程序編寫者來(lái)確定;
- TCP連接建立起來(lái)以后,客戶端之間應(yīng)該開(kāi)始鑒權(quán)操作,確保目前聯(lián)入的連接就是所希望的連接。如果鑒權(quán)失敗,客戶端將關(guān)閉連接,并且繼續(xù)等待新的連接聯(lián)入。客戶端通常采用“先入為主”的策略,只接受第一個(gè)通過(guò)鑒權(quán)操作的客戶端,然后將進(jìn)入P2P通信過(guò)程不再繼續(xù)等待是否有新的連接聯(lián)入。
圖7 - TCP打洞
與UDP不同的是,因?yàn)槭褂肬DP協(xié)議的每個(gè)客戶端只需要一個(gè)套接字即可完成與服務(wù)器的通信,而TCP客戶端必須處理多個(gè)套接字綁定到同一個(gè)本地TCP端口的問(wèn)題,如圖7所示。現(xiàn)在來(lái)看實(shí)際中常見(jiàn)的一種情景,A與B分別位于不同的NAT設(shè)備后面,如圖5所示,并且假定圖中的端口號(hào)是TCP協(xié)議的端口號(hào),而不是UDP的端口號(hào)。圖中向外的連接代表A和B向?qū)Ψ降膬?nèi)網(wǎng)地址二元組發(fā)起的連接,這些連接或許會(huì)失敗或者無(wú)法連接到對(duì)方。如同使用UDP協(xié)議進(jìn)行“打洞”操作遇到的問(wèn)題一樣,TCP的“打洞”操作也會(huì)遇到內(nèi)網(wǎng)的IP與“偽”公網(wǎng)IP重復(fù)造成連接失敗或者錯(cuò)誤連接之類的問(wèn)題。
客戶端向彼此公網(wǎng)地址二元組發(fā)起連接的操作,會(huì)使得各自的NAT設(shè)備打開(kāi)新的“洞”允許A與B的TCP數(shù)據(jù)通過(guò)。如果NAT設(shè)備支持TCP“打洞”操作的話,一個(gè)在客戶端之間的基于TCP協(xié)議的流通道就會(huì)自動(dòng)建立起來(lái)。如果A向B發(fā)送的第一個(gè)SYN包發(fā)到了B的NAT設(shè)備,而B(niǎo)在此前沒(méi)有向A發(fā)送SYN包,B的NAT設(shè)備會(huì)丟棄這個(gè)包,這會(huì)引起A的“連接失敗”或“無(wú)法連接”問(wèn)題。而此時(shí),由于A已經(jīng)向B發(fā)送過(guò)SYN包,B發(fā)往A的SYN包將被看作是由A發(fā)往B的包的回應(yīng)的一部分,所以B發(fā)往A的SYN包會(huì)順利地通過(guò)A的NAT設(shè)備,到達(dá)A,從而建立起A與B的P2P連接。
3 從應(yīng)用程序的角度來(lái)看TCP“打洞”
從應(yīng)用程序的角度來(lái)看,在進(jìn)行TCP“打洞”的時(shí)候都發(fā)生了什么呢?假定A首先向B發(fā)出SYN包,該包發(fā)往B的公網(wǎng)地址二元組,并且被B的NAT設(shè)備丟棄,但是B發(fā)往A的公網(wǎng)地址二元組的SYN包則通過(guò)A的NAT到達(dá)了A,然后,會(huì)發(fā)生以下的兩種結(jié)果中的一種,具體是哪一種取決于操作系統(tǒng)對(duì)TCP協(xié)議的實(shí)現(xiàn):
(1)A的TCP實(shí)現(xiàn)會(huì)發(fā)現(xiàn)收到的SYN包就是其發(fā)起連接并希望聯(lián)入的B的SYN包,通俗一點(diǎn)來(lái)說(shuō)就是“說(shuō)曹操,曹操到”的意思,本來(lái)A要去找B,結(jié)果B自己找上門來(lái)了。A的TCP協(xié)議棧因此會(huì)把B作為A向B發(fā)起連接connect的一部分,并認(rèn)為連接已經(jīng)成功。程序A調(diào)用的異步connect()函數(shù)將成功返回,A的listen()等待從外部聯(lián)入的函數(shù)將沒(méi)有任何反映。此時(shí),B聯(lián)入A的操作在A程序的內(nèi)部被理解為A聯(lián)入B連接成功,并且A開(kāi)始使用這個(gè)連接與B開(kāi)始P2P通信。
由于收到的SYN包中不包含A需要的ACK數(shù)據(jù),因此,A的TCP將用SYN-ACK包回應(yīng)B的公網(wǎng)地址二元組,并且將使用先前A發(fā)向B的SYN包一樣的序列號(hào)。一旦B的TCP收到由A發(fā)來(lái)的SYN-ACK包,則把自己的ACK包發(fā)給A,然后兩端建立起TCP連接。簡(jiǎn)單的說(shuō),第一種,就是即使A發(fā)往B的SYN包被B的NAT丟棄了,但是由于B發(fā)往A的包到達(dá)了A。結(jié)果是,A認(rèn)為自己連接成功了,B也認(rèn)為自己連接成功了,不管是誰(shuí)成功了,總之連接是已經(jīng)建立起來(lái)了。
(2)另外一種結(jié)果是,A的TCP實(shí)現(xiàn)沒(méi)有像(1)中所講的那么“智能”,它沒(méi)有發(fā)現(xiàn)現(xiàn)在聯(lián)入的B就是自己希望聯(lián)入的。就好比在機(jī)場(chǎng)接人,明明遇到了自己想要接的人卻不認(rèn)識(shí),誤認(rèn)為是其他的人,安排別人給接走了,后來(lái)才知道是自己錯(cuò)過(guò)了機(jī)會(huì),但是無(wú)論如何,人已經(jīng)接到了任務(wù)已經(jīng)完成了。然后,A通過(guò)常規(guī)的listen()函數(shù)和accept()函數(shù)得到與B的連接,而由A發(fā)起的向B的公網(wǎng)地址二元組的連接會(huì)以失敗告終。盡管A向B的連接失敗,A仍然得到了B發(fā)起的向A的連接,等效于A與B之間已經(jīng)聯(lián)通,不管中間過(guò)程如何,A與B已經(jīng)連接起來(lái)了,結(jié)果是A和B的基于TCP協(xié)議的P2P連接已經(jīng)建立起來(lái)了。
第一種結(jié)果適用于基于BSD的操作系統(tǒng)對(duì)于TCP的實(shí)現(xiàn),而第二種結(jié)果更加普遍一些,多數(shù)Linux和Windows系統(tǒng)都會(huì)按照第二種結(jié)果來(lái)處理。
5、本文小結(jié)
在IP地址極度短缺的今天,NAT幾乎已經(jīng)是無(wú)所不在的一項(xiàng)技術(shù)了,以至于現(xiàn)在任何一項(xiàng)新技術(shù)都不得不考慮和NAT的兼容。作為當(dāng)下應(yīng)用最廣泛的技術(shù)之一,P2P技術(shù)也必然要面對(duì)NAT這個(gè)障礙。
打洞技術(shù)看起來(lái)是一項(xiàng)近似乎蠻干的技術(shù),卻不失為一種有效的技術(shù)手段。在集中服務(wù)器的幫助下,P2P的雙方利用端口預(yù)測(cè)的技術(shù)在NAT網(wǎng)關(guān)上打出通道,從而實(shí)現(xiàn)NAT穿越,解決了NAT對(duì)于P2P的阻隔,為P2P技術(shù)在網(wǎng)絡(luò)中更廣泛的推廣作出了非常大的貢獻(xiàn)。
-
通信
+關(guān)注
關(guān)注
18文章
6024瀏覽量
135950 -
服務(wù)器
+關(guān)注
關(guān)注
12文章
9123瀏覽量
85324 -
網(wǎng)絡(luò)存儲(chǔ)
+關(guān)注
關(guān)注
1文章
61瀏覽量
25278 -
UDP協(xié)議
+關(guān)注
關(guān)注
0文章
69瀏覽量
12694
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論