UDP"打洞"原理

1.?????? NAT分類

根據Stun協議(RFC3489),NAT大致分為下面四類

1)????? Full Cone

這種NAT內部的機器A連接過外網機器C后,NAT會打開一個端口.然后外網的任何發到這個打開的端口的UDP數據報都可以到達A.不管是不是C發過來的.

例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88

A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)

任何發送到 NAT(202.100.100.100:8000)的數據都可以到達A(192.168.8.100:5000)

2)????? Restricted Cone

這種NAT內部的機器A連接過外網的機器C后,NAT打開一個端口.然后C可以用任何端口和A通信.其他的外網機器不行.

例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88

A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)

任何從C發送到 NAT(202.100.100.100:8000)的數據都可以到達A(192.168.8.100:5000)

?

3)????? Port Restricted Cone

這種NAT內部的機器A連接過外網的機器C后,NAT打開一個端口.然后C可以用原來的端口和A通信.其他的外網機器不行.

例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88

A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)

C(202.88.88.88:2000)發送到 NAT(202.100.100.100:8000)的數據都可以到達A(192.168.8.100:5000)

?

以上三種NAT通稱Cone NAT.我們只能用這種NAT進行UDP打洞.

4)????? Symmetic

對于這種NAT.連接不同的外部目標.原來NAT打開的端口會變化.而Cone NAT不會.雖然可以用端口猜測.但是成功的概率很小.因此放棄這種NAT的UDP打洞.

2.?????? UDP hole punching

對于Cone NAT.要采用UDP打洞.需要一個公網機器C來充當”介紹人”.內網的A,B先分別和C通信.打開各自的NAT端口.C這個時候知道A,B的公網 IP:Port. 現在A和B想直接連接.比如A給B發.除非B是Full Cone.否則不能通信.反之亦然.但是我們可以這樣.

A要連接B.A給B發一個UDP包.同時.A讓那個介紹人給B發一個命令,讓B同時給A發一個UDP包.這樣雙方的NAT都會記錄對方的IP,然后就會允許互相通信.

3.?????? 同一個NAT后面的情況

如果A,B在同一個NAT后面.如果用上面的技術來進行互連.那么如果NAT支持loopback(就是本地到本地的轉換),A,B可以連接,但是比較浪費帶寬和NAT.有一種辦法是,A,B和介紹人通信的時候,同時把自己的local IP也告訴服務器.A,B通信的時候,同時發local ip和公網IP.誰先到就用哪個IP.但是local ip就有可能不知道發到什么地方去了.比如A,B在不同的NAT后面但是他們各自的local ip段一樣.A給B的local IP發的UDP就可能發給自己內部網里面的某某某了.

還有一個辦法是服務器來判斷A,B是否在一個NAT后面.(網絡拓樸不同會不會有問題?)

--------------------------------------------------------------------------------------------------------------------------

From: http://hi.baidu.com/ligh0721/blog/item/0cbbc9384106252b96ddd872.html

前天編程做了一下UDP打洞的實驗,今天特寫了一篇文章總結一下。

我們知道網絡上兩個主機進行通信,如果其中一臺主機擁有公網IP那么,那么進行會話是比較簡單的,但是如果兩臺主機是位于不同內網之中的,那么應該如何進行通信呢。一種想法是再找一臺公網的服務器,用來轉發信息,但是這有一個問題,就是會給服務器帶來壓力,因此我們就來談談一種用于不同內網中的主機互相通信的一個解決方案——NAT打洞。

原理還是比較簡單的,我們先了解一下什么是“NAT的洞”。當處于內網中的一臺主機(ClientA/192.168.1.128)向一個公網的服務器 (Server)發送數據時,這時NAT(NAT1)將會打開一個臨時性的端口用于與公網的服務器進行通信,并且會把那個內網主機發送出的IP數據報的頭部中源IP地址改為NAT的公網IP(218.7.32.28),將TCP或UDP數據報中源端口(2347)改為那個臨時端口(26756)這樣就實現了由 “192.168.1.128:2347”到“218.7.32.28:26756”的源地址源端口的轉換。這個數據包到達公網服務器后,服務器就可以根據這個包的頭部信息進行回復。當服務器的數據包到達NAT后,NAT在將這個數據發送到內網主機192.168.1.128的2347端口。那么這個 NAT上的26756端口我們就稱作“洞”。如果這個NAT不是Full Cone NAT的話(其實大多數的NAT確實不是這種類型的),那么我們說這個“洞”是有方向性的。一個洞應該會指向一個(也可以是多個)公網主機的IP地址。比如上面說的例子,在NAT1上打的洞是指向Server的IP地址。來自其他公網主機發向這個洞(也就是218.7.32.28:26756)的數據包會被非Full Cone類型的NAT所丟棄。所以如果有另一臺處于另一內網的主機(ClientB/192.168.0.5)向218.7.32.28:26756直接發送數據的話,同樣也會被NAT1丟棄。

那么如何建立ClientA和ClientB的直接會話呢?
網絡環境描述:
內網1NAT:NAT1/218.7.32.28
內網1中一臺主機:ClientA/192.168.1.128
內網2NAT:NAT2/218.7.31.221
內網2中一臺主機:ClientB/192.168.0.5
公網服務器:Server

首先讓ClientA和ClientB登錄到服務器Server(假如兩臺主機都采用2347端口),此時NAT1和NAT2會分別為ClientA和 ClientB打開一個指向Server的洞(NAT1上218.7.32.28:26756和NAT2上218.7.31.221:27550)。服務器應改記錄這兩個客戶端的信息(關鍵是那兩個洞的信息)。當ClientA與ClientB要建立會話時,ClientA首先用2347端口向NAT2的洞發送一個數據包,當然這個數據包會被NAT2所丟棄,但是由于這是從NAT1內部向外部發送數據,所以NAT1為ClientA打開了一個指向NAT2 的洞。而且這個新洞與原來NAT1上指向Server的舊洞的是同一個洞(因為是同一個端口26756),所以這里可以說這個洞具有了兩個方向,同時指向 Server和NAT2。這時ClientA應該通知Server,告訴ClientB,現在可以向NAT1的那個洞 (218.7.32.28:26756)發送數據包了。當ClientB向NAT1的那個洞發送數據以后,NAT2也為ClientB打了一個指向 NAT1的洞,這是可以說ClientA與ClientB的會話就建立完成了,他們可以不依賴Server進行通信了。如果以后ClientA和 ClientB還需要建立其他會話 ,那么這個牽線的“媒人”可以不是Server,而可以是ClientA或ClientB了。

UDP打洞可以實現不同內網內的主機進行通信,而且實施性比較高,一般用于P2P通信。這也就是為什么常會看見騰訊QQ在開始傳輸文件時會顯示“UDP連接已經建立”了