1. 程式人生 > >穿透內網,連線動態ip,內網ip打洞-----p2p實現原理

穿透內網,連線動態ip,內網ip打洞-----p2p實現原理

轉自:http://www.cnblogs.com/eyye/archive/2012/10/23/2734807.html

網上找了很多,程式碼大堆,原理講清楚透徹的不多。

本人找幾篇講得好的來整理一下。

一片技術文章,最主要的講清楚原理,如果再有完整的能執行的原始碼也可,關鍵是要把核心部分程式碼分析清楚。

(1)問題的由來:

大部分的電腦上網都是用動態ip地址。內網的ip是由net(路由、閘道器)分配的,net發出去的時候,對映到一個公網地址,這是一個動態計算的過程(特別是埠號),因此稱為動態ip地址。外部網是無法直接訪問內網計算機的,但在大部分情況下,藉助一臺有公網ip地址電腦(這裡叫伺服器),通過某種方式,可以實現連線,這種技術較“打洞”。

(2)動態ip具體分析:

//TODO: 一個原理圖

如圖:

有一個私有網路192.168.0.2,client a是其中的一臺計算機,這個網路的閘道器natA(一個nat裝置)的外網ip是202.103.142.29(應該還有一個內網的ip地址,比如10.0.0.10)。如果client a中的某個程序(這個程序建立了一個socket,這個socket繫結1234埠)想訪問外網主機129.208.12.38的2000埠,那麼當資料包通過nat時會發生什麼事情呢?

先nat會改變這個資料包的原ip地址,改為202.103.142.29。接著nat會為這個傳輸建立一個session(session是一個抽象的概念,如果是tcp,也許session是由一個syn包開始,以一個fin包結束。而udp呢,以這個ip的這個埠的第一個udp開始,結束不確定,也許是幾分鐘,也許是幾小時,這要看具體的實現了)並且給這個session分配一個埠,比如62000,然後改變這個資料包的源埠為62000。所以本來是

                                  (192.168.0.2:1234->129.208.12.38:2000)

的資料包到了網際網路上變為了

                                   (202.103.142.29:62000->129.208.12.38:2000)。

一旦nat建立了一個session後,nat會記住62000埠對應的是192.168.0.2的1234埠,以後從129.208.12.38傳送到62000埠的資料會被nat自動的轉發到192.168.0.2上。(注意:這裡是說129.208.12.38傳送到62000埠的資料會被轉發,其他的ip傳送到這個埠的資料將被nat拋棄,這就是真正頭痛的問題)這樣client a就與server s1建立以了一個連線。

如果client a的原來那個socket(綁定了1234埠的那個udp socket)又接著向另外一個伺服器server s2傳送了一個udp包,那麼這個udp包在通過nat時會怎麼樣呢?

答案是net決定的,不同的net有不同的答案。

這裡需要介紹一下NAT的型別: NAT裝置的型別對於TCP穿越NAT,有著十分重要的影響,根據埠對映方式,NAT可分為如下4類,前3種NAT型別可統稱為cone型別。 (1)全克隆( Full Cone) : NAT把所有來自相同內部IP地址和埠的請求對映到相同的外部IP地址和埠。任何一個外部主機均可通過該對映傳送IP包到該內部主機。 (2)限制性克隆(Restricted Cone) : NAT把所有來自相同內部IP地址和埠的請求對映到相同的外部IP地址和埠。但是,只有當內部主機先給IP地址為X的外部主機發送IP包,該外部主機才能向該內部主機發送IP包。 (3)埠限制性克隆( Port Restricted Cone) :埠限制性克隆與限制性克隆類似,只是多了埠號的限制,即只有內部主機先向IP地址為X,埠號為P的外部主機發送1個IP包,該外部主機才能夠把源埠號為P的IP包傳送給該內部主機。 (4)對稱式NAT ( Symmetric NAT) :這種型別的NAT與上述3種類型的不同,在於當同一內部主機使用相同的埠與不同地址的外部主機進行通訊時, NAT對該內部主機的對映會有所不同。對稱式NAT不保證所有會話中的私有地址和公開IP之間繫結的一致性。相反,它為每個新的會話分配一個新的埠號。

第一種最為理想,基本就是無需打洞;

第四種最糟糕,根本就不能打洞。好訊息就是這種net基本沒有,很少。

所以關鍵是第二和第三種net型別。

因此打洞的本質就是利用net的特性“只有當內部主機先給IP地址為X的外部主機發送IP包,該外部主機才能向該內部主機發送IP包”

(3)實現步驟(各個實現可能都不一樣)

我們先假設一下:有一個伺服器S在公網上有一個IP,兩個私網分別由NAT-A和NAT-B連線到公網,NAT-A後面有一臺客戶端A,NAT-B 後面有一臺客戶端B,現在,我們需要藉助S將A和B建立直接的TCP連線,即由B向A打一個洞,讓A可以沿這個洞直接連線到B主機,就好像NAT-B不存在一樣。 實現過程如下: 1、 S啟動兩個網路偵聽,一個叫【主連線】偵聽,一個叫【協助打洞】的偵聽。 2、 A和B分別與S的【主連線】保持聯絡。 3、 當A需要和B建立直接的TCP連線時,首先連線S的【協助打洞】埠,併發送協助連線申請。同時在該埠號上啟動偵聽(保證net型別3也能成功)。注意由於要在相同的網路終端上繫結到不同的套接字上,所以必須為這些套接字設定 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連線建立起來了。

(4)讓內網主機做伺服器

以上的應用主要在於做p2p軟體,如果我們想用家裡的電腦,做伺服器,是不需要這項技術(“打洞”)的,應為我們可以把路由器設定為net1型,可以直接設定埠對映:

但還是需要一個外網固定ip的伺服器來負責通知其他客服端,因為撥號時得到的ip(這其實也是外網ip,就是其他的計算機可以直接訪問你的)每次還是變化的。

(5)花生殼是什麼

花生殼是可以把這個變化的ip對映為固定域名的域名服務商,如果不需要域名服務,只要用ip訪問即可的應用(比如資料庫),是用不上這個軟體的。

但是還有一種辦法,如果你已經有一臺有靜態ip的伺服器,在上面部署一個代理伺服器(比如nginx),然後把這個動態的ip通知它就可以了。

--------------------- 本文來自 suhuaiqiang_janlay 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/suhuaiqiang_janlay/article/details/60466333?utm_source=copy