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