面向P2P通信的UDP Hole Punching技術研究與實現
商光山 徐林
摘 要 NAT(Network Address Translation),即網絡地址轉換,一定程度上解決了IPv4網絡地址匱乏的問題,但也給面向P2P通信的應用程序以及協議造成通信方面的障礙。本文介紹了網絡地址轉換的基本原理以及其對P2P通信方式造成的沖擊,簡單概括目前主要的NAT穿透(NAT Traversal)技術,著重分析了UDP Hole Punching的穿透過程,最后給出一個輕量級的實現。 關鍵詞 P2P;NAT 穿透;UDP Hole Punching
1 引言 隨著Internet技術的迅猛發展,公網地址變的越來越珍貴,每臺計算機都分配一個公網地址顯得不切實際。NAT(Network Address Translation)標準出現,一定程度上解決了公網地址緊缺的問題。它是一種把內部私有網絡地址翻譯成合法網絡地址的技術。它允許內部節點在內部網絡中使用內部地址,而當內部節點要與外部網絡進行通訊時,通過具有NAT功能(通常被集成到路由器、防火墻、ISDN路由器或者單獨的NAT設備中)的設備、軟件(統稱NATs-Network Address Translator)將內部地址替換成公網地址,從而在外部公網上正常使用。NAT根據其轉換方式,主要有三種類型:靜態NAT、動態地址NAT、網絡地址端口轉換NAPT。其中靜態NAT把內部網絡中的每個主機都被永久映射成外部網絡中的某個合法的地址。而動態地址NAT則是在外部網絡中定義了一系列的合法地址,采用動態分配的方法映射到內部網絡。NAPT是把內部地址映射到外部網絡的一個IP地址的不同端口上,該地址映射會在NAT設備上保持一定的時間,NAPT是最常見的NAT類型,因為它允許私有網絡后的主機共享一個公用IP地址,有效的節省了費用。無論是哪種NAT類型,從傳輸層觀察,當內部節點向外部網絡發送數據包時,NAT設備都是將數據包中的內部網絡IP地址與端口(稱作“端點地址”)替換為外部合法的端點地址,反之亦然。 NAT的特質屏蔽了內部網絡,所有內部網計算機對于外部網絡來說都是不可見的。在C/S 的應用模型中,服務器位于公網中,客戶端位于公網或NAT設備后的私有網絡,客戶端只是主動的與服務器通信,客戶端之間并不需要通信,所以NAT在C/S 的應用模型中并不構成問題。但是,面向P2P通信(這里的P2P通信不單單指的是P2P應用程序,任何需要在通信雙方“直接互連”的地方,都可稱之為P2P通信)的應用系統中,特別的,當需要通信的雙方位于NAT設備后的不同私有網絡中,任何一方相對與另一方來說都是不可見的,這樣造成雙方無法建立直接的相互可達的通信連接。 目前,解決這種由于NAT存在造成的通信障礙的主要技術有:UPnP(Universal Plug and Play)、STUN(Simple Traversal of UDP over NATs)以及UDP/TCP Hole Punching等。其中,UDP/TCP Hole Punching技術有效的保持了NAT網絡環境的透明性,它不需要了解網絡的拓撲信息以及其它特別的軟件環境的支持,可以被普通的應用程序實現。實驗表明,82%的NAT網絡環境支持UDP Hole Punching技術,雖然其引入了冗余的消息傳遞以及延時,但不失為一個優秀的NAT穿透解決方案。 2 UDP Hole Punching技術研究 UDP Hole Punching的主要思想是:利用一個任何客戶端都可達的服務器,在服務器上事先存儲、維護客戶端的UDP公用端點地址,當雙方需要通信時,可以通過服務器的“介紹”獲取對方的端點地址,建立“直接”的連接。 圖1是一個具有NAT設備的簡單網絡拓撲圖,其中C1,C2位于不同的私有網絡中,無法直接通信,但都可以訪問Server。下面分析UDP Hole Punching的基本流程: (1)C1向服務器發出請求,要求與C2建立連接。 (2)服務器向C1,C2發送對方的公用端點地址,公用端點地址是服務器根據C1,C2發送的UDP數據報的源端點地址“觀察”到的,即NAT設備“翻譯”后的端點地址。 (3)C2接收到C1 的端點地址后,考慮到其所處網絡的NAT設備可能具有防火墻功能,利用C1的端點地址作為目的地址,發送“穿洞”UDP數據報,該數據報使得C2網絡的NAT設備允許后續的以C1端點地址為源地址的UDP數據報進入其內部網絡,像是在NAT設備的防火墻上“穿洞”,對以C1端點地址為源地址的UDP數據報開放“入口”。 (4)C1接收到C2 的端點地址后,以C2的端點地址為目的地址發送“探測”UDP數據報,同樣該數據報也使得C1網絡的NAT設備上形成“穿洞”。 (5)C2收到C1的“探測”數據報后,向C1發送確認數據報。至此,雙方建立互通的UDP連接。 在(2)中,考慮到C1、C2可能處于同一個私有網絡中,服務器可以同時發送私有、公用端點地址,然后C1、C2先嘗試使用對方私有端點地址進行通信,失敗的情況下,再使用公用端點地址。
需要指出的是,并不是任何的NAT設備都支持這上述的“穿透”過程。一個“友好”的NAT設備必須是“非對稱”的:客戶端的私有端點地址被NAT設備“翻譯”成公用端點地址后,客戶端再使用此私有端點地址向其它目的端點地址發送數據報,NAT設備并不會因為目的端點地址的改變而改變映射的公用端點地址,否則,C1、C2獲取的對方端點地址因為“映射”改變而失效。同時,對于復雜的具有多層NAT設備的網絡環境,NAT設備也需提供
本文基于.Net框架開發一個輕量級的面向對象類庫,完整的實現UDP Hole Punching 的穿透過程。圖2是類庫(灰底框)分層結構,描述了類庫邊界以及體系結構。其中NAT Client/Server API 層是類庫對外提供的服務器與客戶端的編程接口;NAT Traversal Layer是整個類庫的核心,處理UDP Hole Punching過程中的各種情境,建立客戶端之間“直接”連接等;Asynchronous UDP Socket是基于UDP的異步收發套接字,為上下層收發數據。 圖3是整個實現的部分類圖(限于篇幅省略了方法與屬性)。下面是部分類功能的簡單描述: (1)客戶端:NATClient 是客戶端的用戶接口,負責客戶端的登陸、登出,向下層轉發字節形式的用戶數據,同時向上層傳遞接收到的用戶數據;ConnectionManager是整個客戶端的核心,它是客戶端多個UDP套接字的管理器,負責啟動穿透過程,接收來自其它客戶端、服務器的各種消息以及作出相應響應。MessageBufferQueue與QueueWithTimer是發送到其它客戶端的用戶數據緩存區,放入緩存區內的數據如果在一個很小的時間間隔內還沒發送出去,將通知客戶端發送失敗。NATSessionManager與NATSession負責管理其它客戶端的端點地址(包括客戶端的私有、公用端點地址);RegisterUtility具有向服務器注冊本地UDP套接字的功能,它能探測出客戶端到服務器之間NAT設備的端點地址映射保持時間長短,動態調整UDP套接字注冊間隔,從而減少客戶端、服務器的負擔。 (2)服務器:NATServer負責客戶端消息的處理,比如注冊消息,請求客戶端的端點地址,通知客戶端準備接收來自其它客戶端的用戶數據等;ClientMapTable 與SocketMapItem 存放、管理客戶端注冊的UDP套接字端點地址。 (3) 其它:根據UDP Hole Punching穿透過程中的各種情境,需要定義多種的消息類型,具有各自的消息負載格式,表1是各種消息類功能說明。
4 結束語 現實的網絡環境中,NAT是一個普遍存在現象,基于P2P通信的應用程序要求參與客戶端的通信對等性,造成多數通信終端無法“直接的”收發數據,因此NAT穿透是基于P2P通信的應用系統必須首要解決的問題。目前,由于NAT設備的多樣性,還沒有一種技術可以“完全”有效解決NAT穿透過程中的各種問題,因此一個健壯的基于P2P通信的系統,需要結合運用多種穿透方法,使得通信終端可以暢通無阻的進行“交流”。