時間過的很快離上次更新文章已經(jīng)有了兩年之久,為了共同學(xué)習(xí)共同進(jìn)步宗旨,繞網(wǎng)絡(luò)模塊主題開展一次技術(shù)分享,(回顧之前同事講了一些主題,HTTP/HTTPS、DNS解析/DNS優(yōu)化、網(wǎng)絡(luò)安全加密、TCP/UDP/SOCKET)具體看看網(wǎng)絡(luò)所包含的模塊吧。

內(nèi)容還真不少,這些東西大多都偏向底層基礎(chǔ),在實(shí)際開發(fā)中大多數(shù)都是由系統(tǒng)或者優(yōu)秀的三方框架來實(shí)現(xiàn)。本著了解底層實(shí)現(xiàn)細(xì)節(jié)知識更能快速掌握上層技術(shù),那么這次分享就圍繞P2P來展開,作為移動端開發(fā)者也為后續(xù)理解WebRTC等技術(shù)積累一點(diǎn)知識。
1)P2P概述
2)P2P應(yīng)用的場景
3)P2P簡單原理
4)NAT概述
5)NAT實(shí)現(xiàn)方式
6)NAT類型
7)NAT類型探測
8)內(nèi)網(wǎng)穿透
P2P概述
P2P應(yīng)用在金融、視頻通話、文件下載,流媒體點(diǎn)播/直播等場景都有廣泛應(yīng)用,日常中我們微信視頻通話,使用迅雷下載文件,中間都使用到了p2p技術(shù)。
P2P,全稱叫做“Peer-to-peer”對等互聯(lián)網(wǎng)絡(luò)技術(shù)(點(diǎn)對點(diǎn)網(wǎng)絡(luò)技術(shù)),它讓用戶可以直接連接到其他用戶的設(shè)備,進(jìn)行數(shù)據(jù)的交換。
舉個簡單例子,比如你喜歡一個女孩子,但是你們互相不認(rèn)識,幸運(yùn)的是剛好有這么一個人,你和女孩都認(rèn)識她。你按捺不住對女孩的想念,你找到這個人拿到了女孩的聯(lián)系方式,然后迫不及待的給這個女孩發(fā)送一條消息,然后你就焦急等待女孩給你回復(fù),女孩接收你得消息開心回復(fù)了你。你和女孩之間聊天就可以理解點(diǎn)對點(diǎn)的方式聊天。當(dāng)然在這里你們是依靠中間人建立的聯(lián)系,在P2P中建立點(diǎn)對點(diǎn)聯(lián)系是依賴集中服務(wù)器。
P2P簡單的原理

我們大部分上網(wǎng)的設(shè)備都處于私網(wǎng)(局域網(wǎng))環(huán)境內(nèi),內(nèi)網(wǎng)客戶端想要上網(wǎng)就需要經(jīng)歷網(wǎng)關(guān)轉(zhuǎn)發(fā)訪問數(shù)據(jù)。服務(wù)器返回?cái)?shù)據(jù)是返回給網(wǎng)關(guān),再由網(wǎng)關(guān)轉(zhuǎn)發(fā)給客戶端。為什么要設(shè)計(jì)成這樣,出于兩個原因的考慮,一是全球范圍內(nèi)上網(wǎng)的設(shè)備很多IP地址不夠,二是對外隱藏客戶端提高安全性(客戶端只能向外訪問,外邊訪問不到客戶端),正因?yàn)榫W(wǎng)絡(luò)環(huán)境的這種設(shè)計(jì)使得點(diǎn)對點(diǎn)通信變得尤為復(fù)雜。
我們知道私網(wǎng)地址是不可以連接外網(wǎng),需要對外訪問只能通過公網(wǎng)地址。客戶端在上網(wǎng)時通過網(wǎng)關(guān)做了NAT(私網(wǎng)地址與公網(wǎng)地址映射,網(wǎng)關(guān)維護(hù)映射表,此時從網(wǎng)關(guān)出去的IP數(shù)據(jù)包源地址替換成了公網(wǎng)地址)從上圖可知道網(wǎng)關(guān)的出接口是公網(wǎng)地址,公網(wǎng)地址是可以被其他設(shè)備訪問到,那么網(wǎng)關(guān)可以訪問服務(wù)器,服務(wù)器也可以訪問網(wǎng)關(guān),網(wǎng)關(guān)與網(wǎng)關(guān)也是可以互相訪問。

那么客戶端A要直接訪問客戶端B,就可以利用網(wǎng)關(guān)訪問到??梢院唵卫斫獬删W(wǎng)關(guān)A與網(wǎng)關(guān)B在通信。要想知道P2P的建立過程,就不得不了解NAT,包括怎樣去實(shí)現(xiàn)內(nèi)網(wǎng)穿透的。
NAT概述
在這里我覺得百度百科講的比我清楚,技術(shù)這個東西自己知道與要講出來甚至寫出來其實(shí)還是很不一樣的,https://baike.baidu.com/item/nat/320024?fr=aladdin如果你不想點(diǎn)開沒關(guān)系,那下面也會做一些NAT簡單引用
NAT技術(shù)誕生背景
要真正了解NAT就必須先了解IP地址的使用情況,私有 IP 地址是指內(nèi)部網(wǎng)絡(luò)或主機(jī)的IP 地址,公有IP 地址是指在因特網(wǎng)上全球地址。RFC 1918 為私有網(wǎng)絡(luò)預(yù)留出了三個IP 地址塊,如下:
A 類:10.0.0.0~10.255.255.255
B 類:172.16.0.0~172.31.255.255
C 類:192.168.0.0~192.168.255.255
上述三個范圍內(nèi)的地址不會在因特網(wǎng)上被分配,因此可以不必向ISP 或注冊中心申請而在公司或企業(yè)內(nèi)部自由使用。
隨著接入Internet的計(jì)算機(jī)數(shù)量的不斷猛增,IP地址資源也就愈加顯得捉襟見肘。事實(shí)上,除了中國教育和科研計(jì)算機(jī)網(wǎng)(CERNET)外,一般用戶幾乎申請不到整段的C類IP地址。在其他ISP那里,即使是擁有幾百臺計(jì)算機(jī)的大型局域網(wǎng)用戶,當(dāng)他們申請IP地址時,所分配的地址也不過只有幾個或十幾個IP地址。顯然,這樣少的IP地址根本無法滿足網(wǎng)絡(luò)用戶的需求,于是也就產(chǎn)生了NAT技術(shù)。
NAT解釋
NAT(Network Address Translation),是指網(wǎng)絡(luò)地址轉(zhuǎn)換。當(dāng)在專用網(wǎng)內(nèi)部的一些主機(jī)本來已經(jīng)分配到了本地IP地址(即僅在本專用網(wǎng)內(nèi)使用的專用地址),但又想和因特網(wǎng)上的主機(jī)通信(并不需要加密)時,可使用NAT方法。
這種方法需要在專用網(wǎng)(私網(wǎng)IP)連接到因特網(wǎng)(公網(wǎng)IP)的路由器上安裝NAT軟件。裝有NAT軟件的路由器叫做NAT路由器,它至少有一個有效的外部全球IP地址(公網(wǎng)IP地址)。這樣,所有使用本地地址(私網(wǎng)IP地址)的主機(jī)在和外界通信時,都要在NAT路由器上將其本地地址轉(zhuǎn)換成全球IP地址,才能和因特網(wǎng)連接。
另外,這種通過使用少量的全球IP地址(公網(wǎng)IP地址)代表較多的私有IP地址的方式,將有助于減緩可用的IP地址空間的枯竭。在RFC 2663中有對NAT的說明。
百度百科講的很專業(yè),讓大家更直觀了解組網(wǎng)有必要提供一個圖

NAT實(shí)現(xiàn)方式
實(shí)現(xiàn)NAT的方式有三種 靜態(tài)轉(zhuǎn)換Static Nat、動態(tài)轉(zhuǎn)換Dynamic Nat和端口多路復(fù)用OverLoad。
靜態(tài)轉(zhuǎn)換Static Nat
靜態(tài)轉(zhuǎn)換分兩種,一種是IP靜態(tài)轉(zhuǎn)換,另一種是端口的靜態(tài)轉(zhuǎn)化。
一、IP靜態(tài)轉(zhuǎn)換
IP靜態(tài)轉(zhuǎn)換是指將內(nèi)部網(wǎng)絡(luò)的私有IP地址轉(zhuǎn)換為公有IP地址,IP地址對是一對一的,是一成不變的,某個私有IP地址只轉(zhuǎn)換為某個公有IP地址。即一個公網(wǎng)地址只能映射一個私網(wǎng)地址,所有外界訪問公網(wǎng)地址都會訪問到這臺固定的設(shè)備上,公網(wǎng)資源全部分配給這個唯一的設(shè)備。對于靜態(tài)NAT并不能節(jié)約公網(wǎng)IP地址使用,因?yàn)橐粚σ混o態(tài)的緣故,因此外界可以訪問進(jìn)來,內(nèi)部也可訪問出去。當(dāng)設(shè)備要對外提供固定的某個服務(wù)就可以使用靜態(tài)NAT。
二、端口靜態(tài)轉(zhuǎn)換
一個IP地址可以有65536個端口,0~1024端口一般分給系統(tǒng)使用,端口靜態(tài)轉(zhuǎn)換是指1024~65536端口每個端口對應(yīng)唯一的私網(wǎng)IP地址某個固定端口,端口對應(yīng)的私網(wǎng)IP地址端口是一成不變的。當(dāng)外界訪問公網(wǎng)地址的某個端口通過網(wǎng)關(guān)對應(yīng)的NAT端口映射表就可以找到具體某個設(shè)備某個端口,因此外界可以訪問某臺設(shè)備固定端口,端口靜態(tài)轉(zhuǎn)化確實(shí)能夠節(jié)省公網(wǎng)IP地址的使用,但是由于端口受限內(nèi)網(wǎng)是無法訪問出去的(由于設(shè)備訪問外界發(fā)送數(shù)據(jù)包源端口地址是隨機(jī)生成,因此你不知道端口號是多少,只有當(dāng)私網(wǎng)地址端口和公網(wǎng)地址端口映射上了才可以訪問外界)
動態(tài)轉(zhuǎn)換Dynamic Nat
動態(tài)IP轉(zhuǎn)化是指內(nèi)部網(wǎng)絡(luò)私有IP地址轉(zhuǎn)化為公有IP地址,公網(wǎng)IP地址來源是從IP池中獲取到。內(nèi)網(wǎng)某臺設(shè)備要向外發(fā)起訪問路由到網(wǎng)關(guān),網(wǎng)關(guān)從IP池中去獲取公網(wǎng)IP地址,若IP池中沒有IP地址就等待其它設(shè)備釋放公網(wǎng)IP,如果能取到公網(wǎng)IP地址,該公網(wǎng)的所有資源內(nèi)網(wǎng)都可以使用,這樣私網(wǎng)設(shè)備就可以訪問外界,當(dāng)訪問結(jié)束會釋放公網(wǎng)IP,因此弊端在某時刻某些設(shè)備可以上網(wǎng)某些設(shè)備不能上網(wǎng)。
端口多路復(fù)用OverLoad
上面講到端口的靜態(tài)轉(zhuǎn)換(簡單說端口關(guān)系是靜態(tài)綁定的),端口多路復(fù)用公網(wǎng)端口和私網(wǎng)端口是臨時映射關(guān)系。內(nèi)網(wǎng)私網(wǎng)地址某個端口向外訪問會映射公網(wǎng)地址某個空閑端口,當(dāng)訪問結(jié)束連接斷開,私網(wǎng)地址端口映射的公網(wǎng)地址端口會被釋放出來給內(nèi)網(wǎng)任意設(shè)備映射臨時關(guān)系。這樣很大程度節(jié)約了IP地址使用,由于零時端口映射關(guān)系,只能內(nèi)網(wǎng)放完外界,外界不能訪問到內(nèi)網(wǎng)設(shè)備,滿足了大批量設(shè)備需要上網(wǎng)的需求,一般園區(qū)網(wǎng),家庭網(wǎng),公司網(wǎng)都是這種組網(wǎng)形式。
NAT類型
了解NAT的實(shí)現(xiàn)方式,我們就可以對NAT進(jìn)行分類。在做內(nèi)網(wǎng)穿透我們就要判斷NAT的類型。
NAT類型可以分為對稱NAT、完全錐形NAT、IP受限NAT、端口受限NAT
一、對稱型NAT
內(nèi)部私網(wǎng)地址訪問外部不同的設(shè)備都會映射一個新的公網(wǎng)地址。(一臺內(nèi)網(wǎng)設(shè)備 對應(yīng)一個公網(wǎng)地址 對應(yīng)一臺外網(wǎng)設(shè)備 產(chǎn)生一對一的關(guān)系)
二、完全錐形NAT
將來自內(nèi)網(wǎng)私有IP地址同一個端口號映射到公網(wǎng)IP某個端口。任意外部IP地址與端口對其自己公網(wǎng)的IP這個映射后的端口訪問,都將定位到內(nèi)部私網(wǎng)地址端口。
例子來源網(wǎng)絡(luò)
例如:
A:192.168.8.100 ——NAT:1.2.3.4 ——C:292.88.88.88
[ok] C(292.88.88.88:2000) ——> NAT(1.2.3.4 : 8000) ——> A(192.168.8.100:5000)
[ok] C(292.88.88.88:3000) ——> NAT(1.2.3.4 : 8000) ——> A(192.168.8.100:5000)
[ok] D(292.99.99.99:3000) ——> NAT(1.2.3.4 : 8000) ——> A(192.168.8.100:5000)
任何發(fā)送到 NAT(202.100.100.100:8000) 的數(shù)據(jù)都可以到達(dá) A(192.168.8.100:5000)
三、IP地址受限錐形NAT
IP地址受限錐形NAT大致可以理解在完全錐形NAT加上了IP訪問限制,并不是所有外部設(shè)備都可以訪問到內(nèi)網(wǎng)設(shè)備,只有通過內(nèi)網(wǎng)設(shè)備對某個外部設(shè)備主動發(fā)起過連接,然后這個外部設(shè)備就可以訪問到內(nèi)網(wǎng)這個設(shè)備,但端口不做限制。
例子來源網(wǎng)絡(luò)
例如:
A:192.168.8.100 ——NAT:1.2.3.4 ——C:292.88.88.88
//可以建立通信
[ok] C(292.88.88.88:2000) ——> NAT(1.2.3.4 : 8000) ——> A(192.168.8.100:5000)
[ok] C(292.88.88.88:3000) ——> NAT(1.2.3.4 : 8000) ——> A(192.168.8.100:5000)
//不能建立通信 如果要建立通信 必須由內(nèi)網(wǎng)設(shè)備發(fā)送數(shù)據(jù)包到 292.99.99.99:3000 建立連接后
[no] D(292.99.99.99:3000) ——> NAT(1.2.3.4 : 8000) ——> A(192.168.8.100:5000)
任何從C發(fā)送到 NAT(1.2.3.4:8000)的數(shù)據(jù)都可以到達(dá)A(192.168.8.100:5000)
四、端口受限錐形NAT
端口受限錐形NAT它比IP地址受限錐形NAT受限更為嚴(yán)格,能接受到外界的數(shù)據(jù)報(bào)范圍更加小了,除了IP受限外還添加了端口受限。內(nèi)網(wǎng)設(shè)備只允許自己外網(wǎng)建立連接的地址端口給自己發(fā)送數(shù)據(jù)報(bào)
例子來源網(wǎng)絡(luò)
例如:
A:192.168.8.100 ——NAT:1.2.3.4 ——C:292.88.88.88
//可以建立通信
[ok] C(292.88.88.88:2000) ——> NAT(1.2.3.4 : 8000) ——> A(192.168.8.100:5000)
//不能建立通信 必須由內(nèi)網(wǎng)設(shè)備發(fā)送數(shù)據(jù)包到 292.88.88.88:3000 建立連接后
[no] C(292.88.88.88:3000) ——> NAT(1.2.3.4 : 8000) ——> A(192.168.8.100:5000)
C(202.88.88.88:2000)發(fā)送到 NAT(1.2.3.4:8000)的數(shù)據(jù)都可以到達(dá)A(192.168.8.100:5000)
NAT類型探測與內(nèi)網(wǎng)穿透
對于NAT類型探測與內(nèi)網(wǎng)穿透詳細(xì)過程可以看 http://www.52im.net/thread-2872-1-1.htm
在實(shí)際的網(wǎng)絡(luò)情況中,我們上網(wǎng)的設(shè)備所處的網(wǎng)絡(luò)環(huán)境不同。處在NAT網(wǎng)下的設(shè)備要進(jìn)行點(diǎn)對點(diǎn)的通信,首先判斷出設(shè)備各自所處的網(wǎng)絡(luò)NAT類型。然后才能根據(jù)NAT的類型實(shí)現(xiàn)相應(yīng)的UDP/TCP打洞。
關(guān)于內(nèi)網(wǎng)穿透建立點(diǎn)對點(diǎn)的連接,已經(jīng)有了下面一些成熟的方法。STUN(Session Traversal Utilities for NAT,NAT會話穿越應(yīng)用程序)技術(shù)就是其中比較重要的一種解決方法,并得到了廣泛的應(yīng)用。除此之外,還有UPNP技術(shù),ICE交互式連接建立,TURN中繼NAT穿越技術(shù)等。
打洞技術(shù)是通過中間服務(wù)器的協(xié)助在各自的NAT網(wǎng)關(guān)上建立相關(guān)的表項(xiàng),使P2P連接的雙方發(fā)送的報(bào)文能夠直接穿透對方的NAT網(wǎng)關(guān),從而實(shí)現(xiàn)P2P客戶端互連。如果兩臺位于NAT設(shè)備后面的P2P客戶端希望在自己的NAT網(wǎng)關(guān)上打個洞,那么他們需要一個協(xié)助者——集中服務(wù)器,并且還需要一種用于打洞的Session建立機(jī)制。
重點(diǎn)介紹一下 客戶端怎么通過STUN(NAT回話穿越應(yīng)用程序的原理)探測出自己的公網(wǎng)地址與NAT的內(nèi)型。STUN協(xié)議 利用STUN客戶端與STUN服務(wù)器(集中服務(wù)器)通信(利用上面所講到NAT類型通信特點(diǎn)來判斷設(shè)備的具體所處的什么NAT網(wǎng)絡(luò)下)
一、NAT類型探測
文字描述不如上圖來的直接

二、NAT穿越方法
1)打洞技術(shù)(Hole Punching)
介紹一下打洞原理:位于NAT后的內(nèi)網(wǎng)設(shè)備節(jié)點(diǎn)需要向外發(fā)送數(shù)據(jù)包,通過NAT建立起內(nèi)網(wǎng)IP地址端口和公網(wǎng)IP地址端口的映射關(guān)系。然后通過某種方式將映射后的外網(wǎng)地址和端口通知給對端節(jié)點(diǎn)設(shè)備。最后對端設(shè)備收到內(nèi)網(wǎng)設(shè)備NAT后的公網(wǎng)地址端口,并向改公網(wǎng)地址端口發(fā)送數(shù)據(jù)包,然后該數(shù)據(jù)包就會被NAT轉(zhuǎn)發(fā)給內(nèi)網(wǎng)設(shè)備。
在這里提出兩個問題,怎么將自己公網(wǎng)地址通知個對端?對端知道自己的公網(wǎng)地址端口就一定能建立通信?

客戶端通過STUN服務(wù)器獲取到自己的公網(wǎng)地址端口NAT類型,再將自己身的信息注冊到中繼服務(wù)器,客戶端1向中繼服務(wù)器發(fā)起獲取客戶端2信息的請求,獲取到客戶端2的信息向客戶端 2網(wǎng)關(guān)發(fā)起直連,獲取對端公網(wǎng)地址已經(jīng)解決了。怎么知道能否建立點(diǎn)對點(diǎn)的通信,其實(shí)上面已經(jīng)討論到了,STUN服務(wù)器探測到客戶端各自的NAT類型,具體使用什么穿越方法參照下表(下圖源于網(wǎng)絡(luò)若侵權(quán)請聯(lián)系刪除)

感興趣的可以自己去分析一下建立點(diǎn)對點(diǎn)通信的步驟。具體的打洞過程包括Relay(服務(wù)器中轉(zhuǎn))技術(shù)可以去看看http://www.52im.net/thread-2872-1-1.htm接下來挑幾個例子來看看具體的打洞過程。
例子1:
客戶端1 主動連接 客戶端2 客戶端1處于任意NAT類型環(huán)境 客戶端2處于IP受限NAT型環(huán)境,客戶端都獲取到了對端信息情況下。

例子2:客戶端1處于任意NAT類型(除對稱NAT外)環(huán)境 客戶端2處于端口受限NAT型

最后介紹一下端口預(yù)測:
端口預(yù)測,就是統(tǒng)計(jì)客戶端多次連接的地址,總結(jié)出客戶端外網(wǎng)端口變化的規(guī)律,然后把預(yù)測出的下一個端口和外網(wǎng)地址發(fā)給對方,然后促成雙方通信。這個方法穿透成功率極低。一般P2P項(xiàng)目再建立點(diǎn)對點(diǎn)連接失敗后都會利用服務(wù)器來進(jìn)行數(shù)據(jù)轉(zhuǎn)發(fā)。
參考文獻(xiàn)
https://baike.baidu.com/item/nat/320024?fr=aladdin
http://www.52im.net/thread-2872-1-1.html
https://blog.csdn.net/WangZekun_wolf/article/details/89102996