P2P的NAT穿越方案

文章來自:http://blog.csdn.net/wutong_login/article/details/8169717

P2P中的NAT穿越方案
王軍
P2P簡介
P2P即點對點通信,或稱爲對等聯網,與傳統的服務器客戶端模式(如左圖所示)有着明顯的區別。P2P可以是一種通信模式、一種邏輯網絡模型、一種技術、甚至一種理念。在P2P網絡中(如右圖所示),所有通信節點的地位都是對等的,每個節點都扮演着客戶機和服務器雙重角色,節點之間通過直接通信實現文件信息、處理器運算能力、存儲空間等資源的共享。P2P網絡具有分散性、可擴展性、健壯性等特點,這使得P2P技術在信息共享、實時通信、協同工作、分佈式計算、網絡存儲等領域都有廣闊的應用。

CS模式 P2P結構模型

NAT技術和P2P技術作爲時下最熱門的兩項網絡技術,在現在的網絡上有着廣泛的應用,P2P主機位於NAT網關後面的情況屢見不鮮。NAT技術雖然在一定程度上解決了IPv4地址短缺的問題,在構建防火牆、保證網絡安全方面都發揮了一定的作用,卻破壞了端到端的網絡通信。NAT阻礙主機進行P2P通信的主要原因是NAT不允許外網主機主動訪問內網主機,但是P2P技術卻要求通信雙方都能主動發起訪問,所以要在NAT網絡環境中進行有效的P2P通信,就必須採用新的解決方案。
P2P作爲一項新興技術,有很大的優化空間,並且相對於網絡設備,基於P2P的應用程序在實現上更爲靈活。所以爲了兼容NAT,基於P2P的應用程序在開發的時候大多會根據自身特點加入一些穿越NAT的功能以解決上述問題。以下着重介紹幾種常見的P2P穿越NAT方案。

一、 反向鏈接技術
適用場景:P2P通信雙方中只有一方位於NAT設備之後
如圖1所示,客戶端A位於NAT之後,它通過TCP端口1234連接到服務器的TCP端口1235上,NAT設備爲這個連接重新分配了TCP端口62000。客戶端B也通過TCP端口1234連接到服務器端口1235上。A和B從服務器處獲知的對方的外網地址二元組{IP地址:端口號}分別爲{138.76.29.7:1234}和{155.99.25.11:62000},它們在各自的本地端口上進行偵聽。
由於B 擁有外網IP地址,所以A要發起與B的通信,可以直接通過TCP連接到B。但如果B嘗試通過TCP連接到A進行P2P通信,則會失敗,原因是A位於NAT設備後,雖然B發出的TCP SYN請求能夠到達NAT設備的端口62000,但NAT設備會拒絕這個連接請求。要想與Client A通信, B不是直接向A發起連接,而是通過服務器給A轉發一個連接請求,反過來請求A連接到B(即進行反向鏈接),A在收到從服務器轉發過來的請求以後,會主動向B發起一個TCP的連接請求,這樣在NAT設備上就會建立起關於這個連接的相關表項,使A和B之間能夠正常通信,從而建立起它們之間的TCP連接。

                                                                                                             圖1 反向鏈接示意圖

二、 UDP打洞技術
適用場景: P2P通信雙方都位於NAT設備之後,且進行基於UDP應用的通信
UDP打洞技術是通過中間服務器的協助在各自的NAT網關上建立相關的表項,使P2P連接的雙方發送的報文能夠直接穿透對方的NAT網關,從而實現P2P客戶端互連。如果兩臺位於NAT設備後面的P2P客戶端希望在自己的NAT網關上打個洞,那麼他們需要一個協助者——集中服務器,並且還需要一種用於打洞的Session建立機制。下面分三種情景介紹該機制的工作過程。

集中服務器
集中服務器本質上是一臺被設置在公網上的服務器,建立P2P的雙方都可以直接訪問到這臺服務器。位於NAT網關後面的客戶端A和B都可以與一臺已知的集中服務器建立連接,並通過這臺集中服務器瞭解對方的信息並中轉各自的信息。
同時集中服務器的另一個重要作用在於判斷某個客戶端是否在NAT網關之後。具體的方法是:一個客戶端在集中服務器上登陸的時候,服務器記錄下該客戶端的兩對地址二元組信息{IP地址:UDP端口},一對是該客戶端與集中服務器進行通信的自身的IP地址和端口號,另一對是集中服務器記錄下的由服務器“觀察”到的該客戶端實際與自己通信所使用的IP地址和端口號。我們可以把前一對地址二元組看作是客戶端的內網IP地址和端口號,把後一對地址二元組看作是客戶端的內網IP地址和端口號經過NAT轉換後的外網IP地址和端口號。集中服務器可以從客戶端的登陸消息中得到該客戶端的內網相關信息,還可以通過登陸消息的IP頭和UDP頭得到該客戶端的外網相關信息。如果該客戶端不是位於NAT設備後面,那麼採用上述方法得到的兩對地址二元組信息是完全相同的。
Session建立機制
假定客戶端A要發起對客戶端B的直接連接,具體的“打洞”過程如下:
(1)A最初不知道如何向客戶端B發起連接,於是A向集中服務器發送消息,請求集中服務器幫助建立與客戶端B的UDP連接。
(2)集中服務器將含有B的外網和內網的地址二元組發給A,同時,集中服務器將包含有A的外網和內網的地址二元組信息的消息也發給B。這樣一來, A與B就都知道對方外網和內網的地址二元組信息了。
(3)當A收到由集中服務器發來的包含B的外網和內網的地址二元組信息後, A開始向B的地址二元組發送UDP數據包,並且A會自動鎖定第一個給出響應的B的地址二元組。同理,當B收到由集中服務器發來的A的外網和內網地址二元組信息後,也會開始向A的外網和內網的地址二元組發送UDP數據包,並且自動鎖定第一個得到A迴應的地址二元組。由於A與B互相向對方發送UDP數據包的操作是異步的,所以A和B發送數據包的時間先後並沒有時序要求。

  1. P2P的兩個客戶端位於同一個NAT設備後面,即位於同一內網中
    這是最簡單的一種情況(如圖4所示)。客戶端A和B分別與集中服務器建立UDP連接,經過NAT轉換後,A的公網端口被映射爲62000,B的公網端口映射爲62005。

                                                                                      圖2 位於同一個NAT設備後的UDP打洞過程
    

    當A向集中服務器發出消息請求與B進行連接,集中服務器將B的外網地址二元組以及內網地址二元組發給A,同時把A的外網以及內網的地址二元組信息發給B。A和B發往對方公網地址二元組信息的UDP數據包不一定會被對方收到,這取決於當前的NAT設備是否支持不同端口之間的UDP數據包能否到達(即Hairpin轉換特性),無論如何A與B發往對方內網的地址二元組信息的UDP數據包是一定可以到達的,內網數據包不需要路由,且速度更快。A與B推薦採用內網的地址二元組信息進行常規的P2P通信。
    假定NAT設備支持Hairpin轉換,P2P雙方也應忽略與內網地址二元組的連接,如果A 和B採用外網的地址二元組做爲P2P通信的連接,這勢必會造成數據包無謂地經過NAT設備,這是一種對資源的浪費。就目前的網絡情況而言,應用程序在“打洞”的時候,最好還是把外網和內網的地址二元組都嘗試一下。如果都能成功,優先以內網地址進行連接。
    什麼是Hairpin技術?
    Hairpin技術又被稱爲Hairpin NAT、Loopback NAT或Hairpin Translation。Hairpin技術需要NAT網關支持,它能夠讓兩臺位於同一臺NAT網關後面的主機,通過對方的公網地址和端口相互訪問,NAT網關會根據一系列規則,將對內部主機發往其NAT公網IP地址的報文進行轉換,並從私網接口發送給目標主機。目前有很多NAT設備不支持該技術,這種情況下,NAT網關在一些特定場合下將會阻斷P2P穿越NAT的行爲,打洞的嘗試是無法成功的。好在現在已經有越來越多的NAT設備商開始加入到對該轉換的支持中來。

  2. P2P客戶端位於不同的NAT設備後面,分屬不同的內網
    這是最普遍的一種情況(如圖3所示)。客戶端A與B經由各自的NAT設備與集中服務器建立UDP連接, A與B的本地端口號均爲4321,集中服務器的公網端口號爲1234。在向外的會話中, A的外網IP被映射爲155.99.25.11,外網端口爲62000;B的外網IP被映射爲138.76.29.7,外網端口爲31000。
    客戶端A——>本地IP:10.0.0.1,本地端口:4321,外網IP:155.99.25.11,外網端口:62000
    客戶端B——>本地IP:10.1.1.3,本地端口:4321,外網IP:138.76.29.7,外網端口:31000

                                                                                        圖3 位於不同NAT設備後的UDP打洞過程
    

    在A向服務器發送的登陸消息中,包含有A的內網地址二元組信息,即10.0.0.1:4321;服務器會記錄下A的內網地址二元組信息,同時會把自己觀察到的A的外網地址二元組信息記錄下來。同理,服務器也會記錄下B的內網地址二元組信息和由服務器觀察到的客戶端B的外網地址二元組信息。無論A與B二者中的任何一方向服務器發送P2P連接請求,服務器都會將其記錄下來的上述的外網和內網地址二元組發送給A或B。
    A和B分屬不同的內網,它們的內網地址在外網中是沒有路由的,所以發往各自內網地址的UDP數據包會發送到錯誤的主機或者根本不存在的主機上。當A的第一個消息發往B的外網地址(如圖3所示),該消息途經A的NAT設備,並在該設備上生成一個會話表項,該會話的源地址二元組信息是{10.0.0.1:4321},和A與服務器建立連接的時候NAT生成的源地址二元組信息一樣,但它的目的地址是B的外網地址。在A的NAT設備支持保留A的內網地址二元組信息的情況下,所有來自A的源地址二元組信息爲{10.0.0.1:4321}的數據包都沿用A與集中服務器事先建立起來的會話,這些數據包的外網地址二元組信息均被映射爲{155.99.25.11:62000}。
    A向B的外網地址發送消息的過程就是“打洞”的過程,從A的內網的角度來看應爲從{10.0.0.1:4321}發往{138.76.29.7:31000},從A在其NAT設備上建立的會話來看,是從{155.99.25.11:62000}發到{138.76.29.7:31000}。如果A發給B的外網地址二元組的消息包在B向A發送消息包之前到達B的NAT設備,B的NAT設備會認爲A發過來的消息是未經授權的外網消息,並丟棄該數據包。
    B發往A的消息包也會在B的NAT設備上建立一個{10.1.1.3:4321,155.99.25.11:62000}的會話(通常也會沿用B與集中服務器連接時建立的會話,只是該會話現在不僅接受由服務器發給B的消息,還可以接受從A的NAT設備{155.99.25.11:6200}發來的消息)。
    一旦A與B都向對方的NAT設備在外網上的地址二元組發送了數據包,就打開了A與B之間的“洞”,A與B向對方的外網地址發送數據,等效爲向對方的客戶端直接發送UDP數據包了。一旦應用程序確認已經可以通過往對方的外網地址發送數據包的方式讓數據包到達NAT後面的目的應用程序,程序會自動停止繼續發送用於“打洞”的數據包,轉而開始真正的P2P數據傳輸。

  3. P2P客戶端位於兩層(或多層)NAT設備之後,最上層的NAT設備通常是由網絡提供商(ISP)提供,下層NAT設備是家用路由器
    (如圖6所示)假定NAT C是由ISP提供的NAT設備,NAT C提供將多個用戶節點映射到有限的幾個公網IP的服務,NAT A和NAT B作爲NAT C的內網節點將把用戶的內部網絡接入NAT C的內網,用戶的內部網絡就可以經由NAT C訪問公網了。從這種拓撲結構上來看,只有服務器與NAT C是真正擁有公網可路由IP地址的設備,而NAT A和NAT B所使用的公網IP地址,實際上是由ISP服務提供商設定的(相對於NAT C而言)內網地址(我們將這種由ISP提供的內網地址稱之爲“僞”公網地址)。同理,隸屬於NAT A與NAT B的客戶端,它們處於NAT A,NAT B的內網,以此類推,客戶端可以放到到多層NAT設備後面。客戶端A和客戶端B發起對服務器S的連接的時候,就會依次在NAT A和NAT B上建立向外的Session,而NAT A、NAT B要聯入公網的時候,會在NAT C上再建立向外的Session。

                                                                                                      圖4 多層NAT下的打洞過程
    

    假定客戶端A和B通過UDP“打洞”完成兩個客戶端的P2P直連,最優化的路由策略是客戶端A向客戶端B的“僞”公網地址(即{10.0.1.2:55000})上發送數據包。從服務器的角度只能觀察到真正的公網地址,也就是NAT A、NAT B在NAT C建立Session真正的公網地址{155.99.25.11:62000}以及{155.99.25.11:62005},但是客戶端A與B無法通過服務器知道這些“僞”公網的地址;即使通過某種手段獲知到了,我們仍然不建議採用上述的“最優化”的打洞方式,這是因爲這些由ISP提供的地址有可能與客戶端本身所在的內網地址重複,這樣就會導致打洞數據包無法發出的問題。
    因此客戶端別無選擇,只能使用由集中服務器觀察到的客戶端A、B的公網地址二元組進行“打洞”操作,用於“打洞”的數據包由NAT C進行轉發。
    當客戶端A向客戶端B的公網地址二元組{155.99.25.11:62005}發送UDP數據包時,NAT A首先把數據包的源地址二元組由A的內網地址二元組{10.0.0.1:4321}轉換爲“僞”公網地址二元組{10.0.1.1:45000},現在數據包到了NAT C,NAT C應該可以識別出來該數據包是要發往自身轉換過的公網地址二元組,如果NAT C可以支持Hairpin的話,NAT C將把該數據包的源地址二元組改爲{155.99.25.11:62000},目的地址二元組改爲{10.0.1.2:55000},即NAT B的“僞”公網地址二元組,NAT B最後會將收到的數據包發往客戶端B。同樣,由B發往A的數據包也會經過類似的過程。

當然,從應用的角度上來說,在完成打洞過程的同時,還有一些技術問題需要解決,如UDP在空閒狀態下的超時問題
由於UDP轉換協議提供的“洞”不是絕對可靠的,多數NAT設備內部都有一個UDP轉換的空閒狀態計時器,如果在一段時間內沒有UDP數據通信,NAT設備會關掉由“打洞”過程打出來的“洞”。如果P2P應用程序希望“洞”的存活時間不受NAT網關的限制,就最好在穿越NAT以後設定一個穿越的有效期。
對於有效期目前沒有標準值,它與NAT設備內部的配置有關,某些設備上最短的只有20秒左右。在這個有效期內,即使沒有P2P數據包需要傳輸,應用程序爲了維持該“洞”可以正常工作,也必須向對方發送“打洞”心跳包。這個心跳包是需要雙方應用程序都發送的,只有一方發送不會維持另一方的Session正常工作。除了頻繁發送“打洞”心跳包以外,還有一個方法就是在當前的“洞”超時之前,P2P客戶端雙方重新“打洞”,丟棄原有的“洞”,這也不失爲一個有效的方法。

三、 TCP打洞技術
從現在的主流應用的角度上來看,基於TCP的P2P應用顯然不如基於UDP的應用那麼廣泛,但是也存在打洞的需求。
TCP相對於UDP而言要複雜的多,TCP連接的建立要依賴於三次握手的交互,所以NAT網關在處理TCP連接的時候,需要更多的開銷。但是基於TCP的P2P打洞技術卻比UDP複雜一點而已,而且,由於TCP協議完備的狀態機機制,TCP反而比UDP更能精確的獲取某個Session的生命期。
在前面提到的各種場景下, TCP打洞技術基本上也是沿用UDP打洞技術的思路。爲了配合TCP在NAT上打洞,需要應用程序支持套接字和TCP端口重用技術。此後P2P通信雙方只要各自向對方發起連接,以期待恰好一次嘗試在雙方的NAT網關上成功的打出一條通道。當然實際的打洞詳細過程會考慮到很多特殊情況的處理方式,讀者可參考相關文章參考,本文對此不贅述。

結束語
在IP地址極度短缺的今天,NAT幾乎已經是無所不在的一項技術了,以至於現在任何一項新技術都不得不考慮和NAT的兼容。作爲當下應用最熱門的技術之一,P2P技術也必然面對NAT這個重鎮。打洞技術看起來是一項近似乎蠻幹的技術,卻不失爲一種有效的技術手段。在集中服務器的幫助下,P2P的雙方利用端口預測的技術在NAT網關上打出通道,從而實現NAT穿越,解決了NAT對於P2P的阻隔,爲P2P技術在網絡中更廣泛的推廣作出了非常大的貢獻。

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