本文章出自cnntec.com的AZ貓著,如需要轉發,請注明來自cnntec.com
Peer-To-Peer縮寫P2P
中文稱之為對等聯網。
用途于交流,比如QQ,MSN等等。
文件傳輸、分布式數據計算等等。
這里我們主要是是簡單講解一下UDP實現NAT的穿透(俗稱打洞)
當然TCP與之相似,可以以此類推。
NAT最開始出現在路由器上。詳細的大家可以在網上查下資料
NAT的全稱是Network Address Translator中文稱之為網絡地址轉換
NAT分為兩大類,NAT和NAPT(Network Address Port Translator)這個不用說了,端口地址轉換。
用于實例,簡單的說,實現P2P需要一個中轉服務器。也就是需要一個第三方。(一會兒我們來說為什么需要一個第三方)
以簡單的通迅來講,首先我們來看一個示例圖。
A<——————>B? A與B之間進行的通迅
A的IP地址為222.182.100.1
B的IP地址為222.182.100.2
如果這兩個用戶都是采用的全球唯一的IP地址,那么他們通迅很簡單,也不需要實現P2P。
A<------------------>Nat<-------------------->B
如果其中一方為內網用戶,及IP地址不為全球唯一IP
就會出現通過路由器進行通迅。
那么在經過路由器的時候,路由器會出現映射IP地址與端口的情況。
如:A為內網用戶。B為外網用戶。則B的IP地圖為全球唯一IP地址。可以直接通迅。
A的IP地址為:192.168.1.100 端口為1025
經過路由器向B進行通迅,路由器將會產生一個一分鐘到幾小時不定的一個Session,這個Session映射了內網A的IP地址及其接收信息的端口。
那么路由向B發送信息的時候,IP地址及端口就變成了222.182.100.1:3645(假設)
這個時候實際上A就是在進行路由NAT的穿透,
如果我們在B向A發送信息的時候采用192.168.1.100:1025這樣的IP和端口,是找不到A的,因為這個IP不是全球唯一IP。
那么B需要的是在收到A的信息的時候,獲取其IP地址和端口,那么獲取到的就是222.182.100.1:3645這個路由器的映射Session地址。
B現在只需要向這個映射地址發送消息,路由器就會自動將消息發送到對應的A方去。否則路由器將當作無用包,將這個消息丟棄。
那如,我們現在就實現了局域網向某單個固定外網機器發送消息。
如果再來一臺C端,也是外網的IP。C通過222.182.100.1:3645向A發送消息,A是否能收到呢?答案是否定的,A不能收到。為什么?因為路由在映射A的穿透時就記錄了B的地址,也就是除了B向這個映射點發送消息可以通向A,其它的地址是不行的。路由器此時會將其當作無用包消息給丟棄掉。
那怎么辦呢?只有A再向C發送一個穿透,C才可以向A發送消息。
以上我們只是說了一點基本的理論。接下來我們要實現什么?不同內網通過internet網進行通迅。
再來,我們舉個圖例
A<----------->NatA<---------->NatB<---------->B
A的地址是:192.168.1.100端口4000
B的地址是:192.168.1.100端口4000
它們兩個都是內網的地址。及局域網內部地址。并不是全球唯一地址。
兩個路由:
NatA的地址是:222.182.100.1
NatB的地址是:222.182.100.2
這兩個路由是外網的地址,及全球唯一地址。
現在我們要實現A與B的通迅。
因為A與B都不是外網地址。所以A不可能向192.168.1.100發送消息。這消息只會它自己收到,因為這個IP是它自己的。同樣B也不可以。
那么A向NatB發送消息,B能收到嗎?答案是否定的,不能收到。剛才我們提到過。因為路由沒有映射B的地址。A并不知道這個Session就連NatB也不知道這個Session因為B沒有向A發送消息,并不產生這個Session。
就算B和A同時向雙方的路由發送消息,產生的Session,A和B也得不到。因為在路由上就把這個消息當做為無用包給丟棄掉了。
那么這樣的情況我們要進行通迅怎么辦呢?
對,就是剛才我們提到的第三方。第三方是個什么方呢?
第三方必須是一個擁有固定外網IP的服務方。及一個外網服務器。全唯一IP地址。
圖例:
假定我們這個第三方為C
C? IP:222.182.100.3端口4001
A<----------->NatA<--------------->C<-------------------->NatB<------------->B
? ? ? ? ? ? ? ? ? ? ↑______________________________↑? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
原理如下
A通過路由向C發送消息,C獲取A的在路由上的Session地址,映射的IP和端口
B同樣。
這時候C就有了A和B的地址。
C可以和A、B進行通迅,但是A和B還不行。
現在C需要通知A方B的映射IP和端口。也要通知B方A的映射IP和端口。
這樣A就有了B的映射地址,B也有了A的。但是現在還不能進行通迅。
因為在路由上A和B都只有對C的穿透。并沒有相互之前的穿透。
那么A要向B發送消息怎么辦呢?需要C向B發送一個消息告訴B方A的地址讓B向這個地址發送一個消息,對A進行一個穿透。
這樣A就可以向B發送消息了。在A向B發送消息的同時,A也在向B進行穿透。
這樣就可以實現相互的通迅了。如果有多個端點,也就以此類推了。
宗上所述就是P2P的UDP實現原理了。TCP也是一樣的。提示一點。Session在路由上是有時限的,一分鐘到幾小時不定。不同的路由不同的時間,為了保持這個Session的存在,你需要在固定時間點進行通迅,保持這個穿透,否則就得重新穿透。
值得注意的一點。
路由上的映射有兩種情況
第一種情況是:Cone NAT
第二種情況是:Symmetric NAT
我們以上的實現是以Cone Nat為基礎的。為什么呢?因為Cone Nat在映射的時候端口是不變的。無論你內網有多少臺機器,向外網發送消息在路由上映射的端口都是不變的。
而Symmetric Nat則相反,一個映射一個端口。如果碰到這種情況只有祝你好運了,最好不要猜。(十有八九猜不到。所以不推薦猜)
Peer-To-Peer縮寫P2P
中文稱之為對等聯網。
用途于交流,比如QQ,MSN等等。
文件傳輸、分布式數據計算等等。
這里我們主要是是簡單講解一下UDP實現NAT的穿透(俗稱打洞)
當然TCP與之相似,可以以此類推。
NAT最開始出現在路由器上。詳細的大家可以在網上查下資料
NAT的全稱是Network Address Translator中文稱之為網絡地址轉換
NAT分為兩大類,NAT和NAPT(Network Address Port Translator)這個不用說了,端口地址轉換。
用于實例,簡單的說,實現P2P需要一個中轉服務器。也就是需要一個第三方。(一會兒我們來說為什么需要一個第三方)
以簡單的通迅來講,首先我們來看一個示例圖。
A<——————>B? A與B之間進行的通迅
A的IP地址為222.182.100.1
B的IP地址為222.182.100.2
如果這兩個用戶都是采用的全球唯一的IP地址,那么他們通迅很簡單,也不需要實現P2P。
A<------------------>Nat<-------------------->B
如果其中一方為內網用戶,及IP地址不為全球唯一IP
就會出現通過路由器進行通迅。
那么在經過路由器的時候,路由器會出現映射IP地址與端口的情況。
如:A為內網用戶。B為外網用戶。則B的IP地圖為全球唯一IP地址。可以直接通迅。
A的IP地址為:192.168.1.100 端口為1025
經過路由器向B進行通迅,路由器將會產生一個一分鐘到幾小時不定的一個Session,這個Session映射了內網A的IP地址及其接收信息的端口。
那么路由向B發送信息的時候,IP地址及端口就變成了222.182.100.1:3645(假設)
這個時候實際上A就是在進行路由NAT的穿透,
如果我們在B向A發送信息的時候采用192.168.1.100:1025這樣的IP和端口,是找不到A的,因為這個IP不是全球唯一IP。
那么B需要的是在收到A的信息的時候,獲取其IP地址和端口,那么獲取到的就是222.182.100.1:3645這個路由器的映射Session地址。
B現在只需要向這個映射地址發送消息,路由器就會自動將消息發送到對應的A方去。否則路由器將當作無用包,將這個消息丟棄。
那如,我們現在就實現了局域網向某單個固定外網機器發送消息。
如果再來一臺C端,也是外網的IP。C通過222.182.100.1:3645向A發送消息,A是否能收到呢?答案是否定的,A不能收到。為什么?因為路由在映射A的穿透時就記錄了B的地址,也就是除了B向這個映射點發送消息可以通向A,其它的地址是不行的。路由器此時會將其當作無用包消息給丟棄掉。
那怎么辦呢?只有A再向C發送一個穿透,C才可以向A發送消息。
以上我們只是說了一點基本的理論。接下來我們要實現什么?不同內網通過internet網進行通迅。
再來,我們舉個圖例
A<----------->NatA<---------->NatB<---------->B
A的地址是:192.168.1.100端口4000
B的地址是:192.168.1.100端口4000
它們兩個都是內網的地址。及局域網內部地址。并不是全球唯一地址。
兩個路由:
NatA的地址是:222.182.100.1
NatB的地址是:222.182.100.2
這兩個路由是外網的地址,及全球唯一地址。
現在我們要實現A與B的通迅。
因為A與B都不是外網地址。所以A不可能向192.168.1.100發送消息。這消息只會它自己收到,因為這個IP是它自己的。同樣B也不可以。
那么A向NatB發送消息,B能收到嗎?答案是否定的,不能收到。剛才我們提到過。因為路由沒有映射B的地址。A并不知道這個Session就連NatB也不知道這個Session因為B沒有向A發送消息,并不產生這個Session。
就算B和A同時向雙方的路由發送消息,產生的Session,A和B也得不到。因為在路由上就把這個消息當做為無用包給丟棄掉了。
那么這樣的情況我們要進行通迅怎么辦呢?
對,就是剛才我們提到的第三方。第三方是個什么方呢?
第三方必須是一個擁有固定外網IP的服務方。及一個外網服務器。全唯一IP地址。
圖例:
假定我們這個第三方為C
C? IP:222.182.100.3端口4001
A<----------->NatA<--------------->C<-------------------->NatB<------------->B
? ? ? ? ? ? ? ? ? ? ↑______________________________↑? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
原理如下
A通過路由向C發送消息,C獲取A的在路由上的Session地址,映射的IP和端口
B同樣。
這時候C就有了A和B的地址。
C可以和A、B進行通迅,但是A和B還不行。
現在C需要通知A方B的映射IP和端口。也要通知B方A的映射IP和端口。
這樣A就有了B的映射地址,B也有了A的。但是現在還不能進行通迅。
因為在路由上A和B都只有對C的穿透。并沒有相互之前的穿透。
那么A要向B發送消息怎么辦呢?需要C向B發送一個消息告訴B方A的地址讓B向這個地址發送一個消息,對A進行一個穿透。
這樣A就可以向B發送消息了。在A向B發送消息的同時,A也在向B進行穿透。
這樣就可以實現相互的通迅了。如果有多個端點,也就以此類推了。
宗上所述就是P2P的UDP實現原理了。TCP也是一樣的。提示一點。Session在路由上是有時限的,一分鐘到幾小時不定。不同的路由不同的時間,為了保持這個Session的存在,你需要在固定時間點進行通迅,保持這個穿透,否則就得重新穿透。
值得注意的一點。
路由上的映射有兩種情況
第一種情況是:Cone NAT
第二種情況是:Symmetric NAT
我們以上的實現是以Cone Nat為基礎的。為什么呢?因為Cone Nat在映射的時候端口是不變的。無論你內網有多少臺機器,向外網發送消息在路由上映射的端口都是不變的。
而Symmetric Nat則相反,一個映射一個端口。如果碰到這種情況只有祝你好運了,最好不要猜。(十有八九猜不到。所以不推薦猜)
UDP打洞流程:
1、客戶端A登錄服務器,服務器將客戶端A的私網和公網終結點記錄下來。
2、客戶端B登陸服務器,服務器將客戶端B的私網和公網終結點記錄下來。
3、服務器將A的公網終結點發給客戶端B。
4、客戶端B向客戶端A發一個數據包
? ? ? ?(此數據包作用:在客戶端B的NAT上建立一個session,
? ? ? ? ?該session為B分配一個端口,即打了一個往A方向的洞口,
? ? ? ? ?以后如果有數據包從公網到達此端口,NAT將會把此數據包直接轉發給客戶端B)。
? ? ? ? ?但此時客戶端A收不到此數據包,因為A端的NAT攔截了這個未曾謀面的數據包。
5、客戶端B向服務器報告:已經向A方向打洞,此時服務器將B的公網終結點發給A,
? ? ? ? ?并命令客戶端A往B方向發一個數據包(作用同第4步)。
6、客戶端A發送一個數據包到客戶端B的公網終結點上。
? ? ? ? ?此時,客戶端B已經接收到A發送過來的這個數據包,
? ? ? ? ?至此打洞成功,以后A和B可以直接互相發送信息了!
?
Server端打洞流程的編寫:
1、 S啟動兩個網絡偵聽,一個叫【主連接】偵聽,一個叫【協助打洞】的偵聽。
2、 A和B分別與S的【主連接】保持聯系。
3、 當A需要和B建立直接的TCP連接時,首先連接S的【協助打洞】端口,并發送協助連接申請。
? ? ? ?同時在該端口號上啟動偵聽。注意由于要在相同的網絡終端上綁定到不同的套接字上,
? ? ? ?所以必須為這些套接字設置 SO_REUSEADDR 屬性(即允許重用),否則偵聽會失敗。
4、 S的【協助打洞】連接收到A的申請后通過【主連接】通知B,
? ? ? ?并將A經過NAT-A轉換后的公網IP地址和端口等信息告訴B。
5、 B收到S的連接通知后首先與S的【協助打洞】端口連接,
? ? ? ?隨便發送一些數據后立即斷開,這樣做的目的是讓S能知道B經過NAT-B轉換后的公網IP和端口號。
6、 B嘗試與A的經過NAT-A轉換后的公網IP地址和端口進行connect,
? ? ? ?根據不同的路由器會有不同的結果,有些路由器在這個操作就能建立連接(例如我用的TPLink R402),
? ? ? ?大多數路由器對于不請自到的SYN請求包直接丟棄而導致connect失敗,
? ? ? ?但NAT-A會紀錄此次連接的源地址和端口號,為接下來真正的連接做好了準備,
? ? ? ?這就是所謂的打洞,即B向A打了一個洞,下次A就能直接連接到B剛才使用的端口號了。
7、 客戶端B打洞的同時在相同的端口上啟動偵聽。
? ? ? ?B在一切準備就緒以后通過與S的【主連接】回復消息“我已經準備好”,
? ? ? ?S在收到以后將B經過NAT-B轉換后的公網IP和端口號告訴給A。
8、 A收到S回復的B的公網IP和端口號等信息以后,開始連接到B公網IP和端口號,
? ? ? ?由于在步驟6中B曾經嘗試連接過A的公網IP地址和端口,NAT-A紀錄了此次連接的信息,
? ? ? ?所以當A主動連接B時,NAT-B會認為是合法的SYN數據,并允許通過,從而直接的TCP連接建立起來了
1、客戶端A登錄服務器,服務器將客戶端A的私網和公網終結點記錄下來。
2、客戶端B登陸服務器,服務器將客戶端B的私網和公網終結點記錄下來。
3、服務器將A的公網終結點發給客戶端B。
4、客戶端B向客戶端A發一個數據包
?
?
?
?
5、客戶端B向服務器報告:已經向A方向打洞,此時服務器將B的公網終結點發給A,
?
6、客戶端A發送一個數據包到客戶端B的公網終結點上。
?
?
?
Server端打洞流程的編寫:
1、 S啟動兩個網絡偵聽,一個叫【主連接】偵聽,一個叫【協助打洞】的偵聽。
2、 A和B分別與S的【主連接】保持聯系。
3、 當A需要和B建立直接的TCP連接時,首先連接S的【協助打洞】端口,并發送協助連接申請。
?
?
4、 S的【協助打洞】連接收到A的申請后通過【主連接】通知B,
?
5、 B收到S的連接通知后首先與S的【協助打洞】端口連接,
?
6、 B嘗試與A的經過NAT-A轉換后的公網IP地址和端口進行connect,
?
?
?
?
7、 客戶端B打洞的同時在相同的端口上啟動偵聽。
?
?
8、 A收到S回復的B的公網IP和端口號等信息以后,開始連接到B公網IP和端口號,
?
?