大家好,我是小林。
最近有個同學問了我這么一個問題:大概就是他的云服務器,為什么ping 不通他的電腦。這次我們就來說說原因。
IPv4地址有限,最大42億個。為了更好的利用這有限的IP數(shù)量,網(wǎng)絡分為局域網(wǎng)和廣域網(wǎng),將IP分為了私有IP和公網(wǎng)IP,一個局域網(wǎng)里的N多臺機器都可以共用一個公網(wǎng)IP,從而大大增加了"可用IP數(shù)量"。
收發(fā)數(shù)據(jù)就像收發(fā)快遞
當我們需要發(fā)送網(wǎng)絡包的時候,在IP層,需要填入源IP地址,和目的IP地址,也就是對應快遞的發(fā)貨地址和收貨地址。
IP報頭里含有發(fā)送和接收IP地址
但是我們家里的局域網(wǎng)內(nèi),基本上都用192.168.xx.xx這樣的私有IP。
如果我們在發(fā)送網(wǎng)絡包的時候,這么填。對方在回數(shù)據(jù)包的時候該怎么回?畢竟千家萬戶人用的都是192.168.0.1,網(wǎng)絡怎么知道該發(fā)給誰?
所以肯定需要將這個192.168.xx私有IP轉(zhuǎn)換成公有IP。
因此在上篇文章最后,留了這么個問題。局域網(wǎng)內(nèi)用的是私有IP,公網(wǎng)用的都是公有IP。一個局域網(wǎng)里的私有IP想訪問局域網(wǎng)外的公有IP,必然要做個IP轉(zhuǎn)換,這是在哪里做的轉(zhuǎn)換呢?
私有IP和公有IP在哪進行轉(zhuǎn)換
答案是NAT設備,全稱Network Address Translation,網(wǎng)絡地址轉(zhuǎn)換?;旧霞矣寐酚善鞫贾С诌@功能。
我們來聊下它是怎么工作的。
NAT的工作原理
為了簡單,我們假設你很富,你家里分到了一個公網(wǎng)IP地址 20.20.20.20,對應配到了你家自帶NAT功能的家用路由器上,你家里需要上網(wǎng)的設備有很多,比如你的手機,電腦都需要上網(wǎng),他們構成了一個局域網(wǎng),用的都是私有IP,比如192.168.xx。其中你在電腦上執(zhí)行ifconfig命令,發(fā)現(xiàn)家里的電腦IP是192.168.30.5。你要訪問的公網(wǎng)IP地址是30.30.30.30。
于是就有下面這樣一張圖
內(nèi)網(wǎng)IP訪問公網(wǎng)IP
當你準備發(fā)送數(shù)據(jù)包的時候,你的電腦內(nèi)核協(xié)議棧就會構造一個IP數(shù)據(jù)包。這個IP數(shù)據(jù)包報頭里的發(fā)送端IP地址填的就是192.168.30.5,接收端IP地址就是30.30.30.30。將數(shù)據(jù)包發(fā)到NAT路由器中。
此時NAT路由器會將IP數(shù)據(jù)包里的源IP地址修改一下,私有IP地址192.168.30.5改寫為公網(wǎng)IP地址20.20.20.20,這叫SNAT(Source Network Address Translation,源地址轉(zhuǎn)換)。并且還會在NAT路由器內(nèi)部留下一條 192.168.30.5 -> 20.20.20.20的映射記錄,這個信息會在后面用到。之后IP數(shù)據(jù)包經(jīng)過公網(wǎng)里各個路由器的轉(zhuǎn)發(fā),發(fā)到了接收端30.30.30.30,到這里發(fā)送流程結束。
SNAT
如果接收端處理完數(shù)據(jù)了,需要發(fā)一個響應給你的電腦,那就需要將發(fā)送端IP地址填上自己的30.30.30.30,將接收端地址填為你的公網(wǎng)IP地址20.20.20.20,發(fā)往NAT路由器。NAT路由器收到公網(wǎng)來的消息之后,會檢查下自己之前留下的映射信息,發(fā)現(xiàn)之前留下了這么一條 192.168.30.5 -> 20.20.20.20記錄,就會將這個數(shù)據(jù)包的目的IP地址修改一下,變成內(nèi)網(wǎng)IP地址192.168.30.5, 這也叫DNAT(Destination Network Address Translation,目的地址轉(zhuǎn)換)。之后將其轉(zhuǎn)發(fā)給你的電腦上。
DNAT
整個過程下來,NAT悄悄的改了IP數(shù)據(jù)包的發(fā)送和接收端IP地址,但對真正的發(fā)送方和接收方來說,他們卻對這件事情,一無所知。
這就是NAT的工作原理。
NAPT的原理
到這里,相信大家都有一個很大的疑問。
局域網(wǎng)里并不只有一臺機器,局域網(wǎng)內(nèi) 每臺機器都在NAT下留下的映射信息都會是 192.168.xx.xx -> 20.20.20.20,發(fā)送消息是沒啥事,但接收消息的時候就不知道該回給誰了。
NAT的問題
這問題相當致命,因此實際上大部分時候不會使用普通的NAT。
那怎么辦呢?
問題出在我們沒辦法區(qū)分內(nèi)網(wǎng)里的多個網(wǎng)絡連接。
于是乎。
我們可以加入其他信息去區(qū)分內(nèi)網(wǎng)里的各個網(wǎng)絡連接,很自然就能想到端口。
但IP數(shù)據(jù)包(網(wǎng)絡層)本身是沒有端口信息的。常見的傳輸層協(xié)議TCP和UDP數(shù)據(jù)報文里才有端口的信息。
TCP報頭有端口號
UDP報頭也有端口號
于是流程就變成了下面這樣子。
當你準備發(fā)送數(shù)據(jù)包的時候,你的電腦內(nèi)核協(xié)議棧就會先構造一個TCP或者UDP數(shù)據(jù)報頭,里面寫入端口號,比如發(fā)送端口是5000,接收端口是3000,然后在這個基礎上,加入IP數(shù)據(jù)報頭,填入發(fā)送端和接收端的IP地址。
那數(shù)據(jù)包長這樣。
數(shù)據(jù)包的構成
假設,發(fā)送端IP地址填的就是192.168.30.5,接收端IP地址就是30.30.30.30。
將數(shù)據(jù)包發(fā)到NAT路由器中。
此時NAT路由器會將IP數(shù)據(jù)包里的源IP地址和端口號修改一下,從192.168.30.5:5000改寫成20.20.20.20:6000。并且還會在NAT路由器內(nèi)部留下一條 192.168.30.5:5000 -> 20.20.20.20:6000的映射記錄。之后數(shù)據(jù)包經(jīng)過公網(wǎng)里各個路由器的轉(zhuǎn)發(fā),發(fā)到了接收端30.30.30.30:3000,到這里發(fā)送流程結束。
NAPT發(fā)送數(shù)據(jù)
接收端響應時,就會在數(shù)據(jù)包里填入發(fā)送端地址是30.30.30.30:3000,將接收端是20.20.20.20:6000,發(fā)往NAT路由器。NAT路由器發(fā)現(xiàn)下自己之前留下過這么一條 192.168.30.5:5000 -> 20.20.20.20:6000的記錄,就會將這個數(shù)據(jù)包的目的IP地址和端口修改一下,變回原來的192.168.30.5:5000。之后將其轉(zhuǎn)發(fā)給你的電腦上。
NAPT接收數(shù)據(jù)
如果局域網(wǎng)內(nèi)有多個設備,他們就會映射到不同的公網(wǎng)端口上,畢竟端口最大可達65535,完全夠用。這樣大家都可以相安無事。
像這種同時轉(zhuǎn)換IP和端口的技術,就是NAPT(Network Address Port Transfer , 網(wǎng)絡地址端口轉(zhuǎn)換 )。
看到這里,問題就來了。
那這么說只有用到端口的網(wǎng)絡協(xié)議才能被NAT識別出來并轉(zhuǎn)發(fā)?
但這怎么解釋ping命令?ping基于ICMP協(xié)議,而ICMP協(xié)議報文里并不帶端口信息。我依然可以正常的ping通公網(wǎng)機器并收到回包。
ping報頭
事實上針對ICMP協(xié)議,NAT路由器做了特殊處理。ping報文頭里有個Identifier的信息,它其實指的是放出ping命令的進程id。
對NAT路由器來說,這個Identifier的作用就跟端口一樣。
另外,當我們?nèi)プグ臅r候,就會發(fā)現(xiàn)有兩個Identifier,一個后面帶個BE(Big Endian),另一個帶個LE(Little Endian)。
其實他們都是同一個數(shù)值,只不過大小端不同,讀出來的值不一樣。就好像同樣的數(shù)字345,反著讀就成了543。這是為了兼容不同操作系統(tǒng)(比如linux和Windows)下大小端不同的情況。
內(nèi)網(wǎng)穿透是什么
看到這里,我們大概也發(fā)現(xiàn)了。使用了NAT上網(wǎng)的話,前提得內(nèi)網(wǎng)機器主動請求公網(wǎng)IP,這樣NAT才能將內(nèi)網(wǎng)的IP端口轉(zhuǎn)成外網(wǎng)IP端口。
反過來公網(wǎng)的機器想主動請求內(nèi)網(wǎng)機器,就會被攔在NAT路由器上,此時由于NAT路由器并沒有任何相關的IP端口的映射記錄,因此也就不會轉(zhuǎn)發(fā)數(shù)據(jù)給內(nèi)網(wǎng)里的任何一臺機器。
舉個現(xiàn)實中的場景就是,你在你家里的電腦上啟動了一個HTTP服務,地址是192.168.30.5:5000,此時你在公司辦公室里想通過手機去訪問一下,卻發(fā)現(xiàn)訪問不了。
那問題就來了,有沒有辦法讓外網(wǎng)機器訪問到內(nèi)網(wǎng)的服務?
有。
大家應該聽過一句話叫,"沒有什么是加中間層不能解決的,如果有,那就再加一層"。
放在這里,依然適用。
說到底,因為NAT的存在,我們只能從內(nèi)網(wǎng)主動發(fā)起連接,否則NAT設備不會記錄相應的映射關系,沒有映射關系也就不能轉(zhuǎn)發(fā)數(shù)據(jù)。
所以我們就在公網(wǎng)上加一臺服務器x,并暴露一個訪問域名,再讓內(nèi)網(wǎng)的服務主動連接服務器x,這樣NAT路由器上就有對應的映射關系。接著,所有人都去訪問服務器x,服務器x將數(shù)據(jù)轉(zhuǎn)發(fā)給內(nèi)網(wǎng)機器,再原路返回響應,這樣數(shù)據(jù)就都通了。這就是所謂的內(nèi)網(wǎng)穿透。
像上面提到的服務器x,你也不需要自己去搭,已經(jīng)有很多現(xiàn)成的方案,花錢就完事了,比如花某殼。
內(nèi)網(wǎng)穿透
到這里,我們就可以回答文章標題的問題。
為什么我在公司里訪問不了家里的電腦?
那是因為家里的電腦在局域網(wǎng)內(nèi),局域網(wǎng)和廣域網(wǎng)之間有個NAT路由器。由于NAT路由器的存在,外網(wǎng)服務無法主動連通局域網(wǎng)內(nèi)的電腦。
兩個內(nèi)網(wǎng)的聊天軟件如何建立通訊
好了,問題就叒來了。
我家機子是在我們小區(qū)的局域網(wǎng)里,班花家的機子也是在她們小區(qū)的局域網(wǎng)里。都在局域網(wǎng)里,且NAT只能從內(nèi)網(wǎng)連到外網(wǎng),那我電腦上登錄的QQ是怎么和班花電腦里的QQ連上的呢?
兩個局域網(wǎng)內(nèi)的服務無法直接連通
上面這個問法其實是存在個誤解,誤以為兩個qq客戶端應用是直接建立連接的。
然而實際上并不是,兩個qq客戶端之間還隔了一個服務器。
聊天軟件會主動與公網(wǎng)服務器建立連接
也就是說,兩個在內(nèi)網(wǎng)的客戶端登錄qq時都會主動向公網(wǎng)的聊天服務器建立連接,這時兩方的NAT路由器中都會記錄有相應的映射關系。當在其中一個qq上發(fā)送消息時,數(shù)據(jù)會先到服務器,再通過服務器轉(zhuǎn)發(fā)到另外一個客戶端上。反過來也一樣,通過這個方式讓兩臺內(nèi)網(wǎng)的機子進行數(shù)據(jù)傳輸。
兩個內(nèi)網(wǎng)的應用如何直接建立連接
上面的情況,是兩個客戶端通過第三方服務器進行通訊,但有些場景就是要拋開第三端,直接進行兩端通信,比如P2P下載,這種該怎么辦呢?
這種情況下,其實也還是離不開第三方服務器的幫助。
假設還是A和B兩個局域網(wǎng)內(nèi)的機子,A內(nèi)網(wǎng)對應的NAT設備叫NAT_A,B內(nèi)網(wǎng)里的NAT設備叫NAT_B,和一個第三方服務器server。
流程如下。
step1和2: A主動去連server,此時A對應的NAT_A就會留下A的內(nèi)網(wǎng)地址和外網(wǎng)地址的映射關系,server也拿到了A對應的外網(wǎng)IP地址和端口。
step3和4: B的操作和A一樣,主動連第三方server,NAT_B內(nèi)留下B的內(nèi)網(wǎng)地址和外網(wǎng)地址的映射關系,然后server也拿到了B對應的外網(wǎng)IP地址和端口。
step5和step6以及step7: 重點來了。此時server發(fā)消息給A,讓A主動發(fā)UDP消息到B的外網(wǎng)IP地址和端口。此時NAT_B收到這個A的UDP數(shù)據(jù)包時,這時候根據(jù)NAT_B的設置不同,導致這時候有可能NAT_B能直接轉(zhuǎn)發(fā)數(shù)據(jù)到B,那此時A和B就通了。但也有可能不通,直接丟包,不過丟包沒關系,這個操作的目的是給NAT_A上留下有關B的映射關系。
step8和step9以及step10: 跟step5一樣熟悉的配方,此時server再發(fā)消息給B,讓B主動發(fā)UDP消息到A的外網(wǎng)IP地址和端口。NAT_B上也留下了關于A到映射關系,這時候由于之前NAT_A上有過關于B的映射關系,此時NAT_A就能正常接受B的數(shù)據(jù)包,并將其轉(zhuǎn)發(fā)給A。到這里A和B就能正常進行數(shù)據(jù)通信了。這就是所謂的NAT打洞。
step11: 注意,之前我們都是用的UDP數(shù)據(jù)包,目的只是為了在兩個局域網(wǎng)的NAT上打個洞出來,實際上大部分應用用的都是TCP連接,所以,這時候我們還需要在A主動向B發(fā)起TCP連接。到此,我們就完成了兩端之間的通信。
NAT打洞
這里估計大家會有疑惑。
端口已經(jīng)被udp用過了,TCP再用,那豈不是端口重復占用(address already in use)?
其實并不會,端口重復占用的報錯常見于兩個TCP連接在不使用SO_REUSEADDR的情況下,重復使用了某個IP端口。而UDP和TCP之間卻不會報這個錯。之所以會有這個錯,主要是因為在一個linux內(nèi)核中,內(nèi)核收到網(wǎng)絡數(shù)據(jù)時,會通過五元組(傳輸協(xié)議,源IP,目的IP,源端口,目的端口)去唯一確定數(shù)據(jù)接受者。當五元組都一模一樣的時候,內(nèi)核就不知道該把數(shù)據(jù)發(fā)給誰。而UDP和TCP之間"傳輸協(xié)議"不同,因此五元組也不同,所以也就不會有上面的問題。
五元組
NAPT還分為好多種類型,上面的nat打洞方案,都能成功嗎?
關于NAPT,確實還細分為好幾種類型,比如完全錐形NAT和限制型NAT啥的,但這并不是本文的重點。所以我就略過了。我們現(xiàn)在常見的都是錐形NAT。上面的打洞方案適用于大部分場景,這其中包括限制最多的端口受限錐形NAT。
null
總結
? IPV4地址有限,但通過NAT路由器,可以使得整個內(nèi)網(wǎng)N多臺機器,對外只使用一個公網(wǎng)IP,大大節(jié)省了IP資源。
? 內(nèi)網(wǎng)機子主動連接公網(wǎng)IP,中間的NAT會將內(nèi)網(wǎng)機子的內(nèi)網(wǎng)IP轉(zhuǎn)換為公網(wǎng)IP,從而實現(xiàn)內(nèi)網(wǎng)和外網(wǎng)的數(shù)據(jù)交互。
? 普通的NAT技術,只會修改網(wǎng)絡包中的發(fā)送端和接收端IP地址,當內(nèi)網(wǎng)設備較多時,將有可能導致沖突。因此一般都會使用NAPT技術,同時修改發(fā)送端和接收端的IP地址和端口。
? 由于NAT的存在,公網(wǎng)IP是無法訪問內(nèi)網(wǎng)服務的,但通過內(nèi)網(wǎng)穿透技術,就可以讓公網(wǎng)IP訪問內(nèi)網(wǎng)服務。一波操作下來,就可以在公司的網(wǎng)絡里訪問家里的電腦。
審核編輯:黃飛
?
評論