原來這是由於IPV4引發的,咱們上網極可能會處在一個NAT設備(無線路由器之類)以後。
NAT設備會在IP封包經過設備時修改源/目的IP地址. 對於家用路由器來講, 使用的是網絡地址端口轉換(NAPT), 它不只改IP, 還修改TCP和UDP協議的端口號, 這樣就能讓內網中的設備共用同一個外網IP. 舉個例子, NAPT維護一個相似下表的NAT表:安全
NAT設備會根據NAT表對出去和進來的數據作修改, 好比將192.168.0.3:8888
發出去的封包改爲120.132.92.21:9202
, 外部就認爲他們是在和120.132.92.21:9202
通訊. 同時NAT設備會將120.132.92.21:9202
收到的封包的IP和端口改爲192.168.0.3:8888
, 再發給內網的主機, 這樣內部和外部就能雙向通訊了, 但若是其中192.168.0.3:8888
== 120.132.92.21:9202
這一映射由於某些緣由被NAT設備淘汰了, 那麼外部設備就沒法直接與192.168.0.3:8888
通訊了。服務器
咱們的設備常常是處在NAT設備的後面, 好比在大學裏的校園網, 查一下本身分配到的IP, 實際上是內網IP, 代表咱們在NAT設備後面, 若是咱們在寢室再接個路由器, 那麼咱們發出的數據包會多通過一次NAT.網絡
國內移動無線網絡運營商在鏈路上一段時間內沒有數據通信後, 會淘汰NAT表中的對應項, 形成鏈路中斷。架構
而國內的運營商通常NAT超時的時間爲5分鐘,因此一般咱們TCP長鏈接的心跳設置的時間間隔爲3-5分鐘。**rest
NAT會有一個機制,全部外界對內網的請求,到達NAT的時候,都會被NAT所丟棄,這樣若是咱們處於一個NAT設備後面,咱們將沒法獲得任何外界的數據。code
可是這種機制有一個解決方案:就是若是咱們A主動往B發送一條信息,這樣A就在本身的NAT上打了一個B的洞。這樣A的這條消息到達B的NAT的時候,雖然被丟掉了,可是若是B這個時候在給A發信息,到達A的NAT的時候,就能夠從A以前打的那個洞中,發送給到A手上了。orm
簡單來說,就是若是A和B要進行通訊,那麼得事先A發一條信息給B,B發一條信息給A。這樣提早在各自的NAT上打了對方的洞,這樣下一次A和B之間就能夠進行通訊了。路由
RFC3489 中將 NAT 的實現分爲四大類:io
Full Cone NAT (徹底錐形 NAT)form
Restricted Cone NAT (限制錐形 NAT ,能夠理解爲 IP 限制,Port不限制)
Port Restricted Cone NAT (端口限制錐形 NAT,IP+Port 限制)
Symmetric NAT (對稱 NAT)
其中徹底最上層的徹底錐形NAT的穿透性最好,而最下層的對稱形NAT的安全性最高。
簡單來說講這4種類型的NAT表明什麼:
至於爲何沒法協調打洞,下面咱們會從STUN和TURN的工做原理來說。
完成了這些STUN Server就會這些基本信息發送回客戶端,而後根據NAT類型,來判斷是否須要TURN服務器協調進行下一步工做。
咱們來說講這兩步具體作了什麼吧:
第一件事就不用說了,其實就是獲得客戶端的請求,把源IP和Port拿到,添加到ICE Candidate中。
假設B是客戶端,C是STUN服務器,C有兩個IP分別爲IP1和IP2(至於爲何要兩個IP,接着往下看):
B向C的IP1的pot1端口發送一個UDP 包。C收到這個包後,會把它收到包的源IP和port寫到UDP包中,而後把此包經過IP1和port1發還給B。這個IP和port也就是NAT的外網 IP和port(若是你不理解,那麼請你去看個人BLOG裏面的NAT的原理和分類),也就是說你在STEP1中就獲得了NAT的外網IP。
熟悉NAT工做原理的朋友能夠知道,C返回給B的這個UDP包B必定收到。若是在你的應用中,向一個STUN服務器發送數據包後,你沒有收到STUN的任何迴應包,那只有兩種可能:一、STUN服務器不存在,或者你弄錯了port。二、你的NAT拒絕一切UDP包從外部向內部經過。
當B收到此UDP後,把此UDP中的IP和本身的IP作比較,若是是同樣的,就說明本身是在公網,下步NAT將去探測防火牆類型,我不想多說。若是不同,說明有NAT的存在,系統進行STEP2的操做。
B向C的IP1發送一個UDP包,請求C經過另一個IP2和PORT(不一樣與SETP1的IP1)向B返回一個UDP數據包(如今知道爲何C要有兩個IP了吧,雖然還不理解爲何,呵呵)。
咱們來分析一下,若是B收到了這個數據包,那說明什麼?說明NAT來着不拒,不對數據包進行任何過濾,這也就是STUN標準中的full cone NAT。遺憾的是,Full Cone Nat太少了,這也意味着你能收到這個數據包的可能性不大。若是沒收到,那麼系統進行STEP3的操做。
B向C的IP2的port2發送一個數據包,C收到數據包後,把它收到包的源IP和port寫到UDP包中,而後經過本身的IP2和port2把此包發還給B。
和step1同樣,B確定能收到這個迴應UDP包。此包中的port是咱們最關心的數據,下面咱們來分析:
若是這個port和step1中的port同樣,那麼能夠確定這個NAT是個CONE NAT,不然是對稱NAT。道理很簡單:根據對稱NAT的規則,當目的地址的IP和port有任何一個改變,那麼NAT都會從新分配一個port使用,而在step3中,和step1對應,咱們改變了IP和port。所以,若是是對稱NAT,那這兩個port確定是不一樣的。
若是在你的應用中,到此步的時候PORT是不一樣的,那麼這個它就是處在一個對稱NAT下了。若是相同,那麼只剩下了restrict cone 和port restrict cone。系統用step4探測是是那一種。
B向C的IP2的一個端口PD發送一個數據請求包,要求C用IP2和不一樣於PD的port返回一個數據包給B。
咱們來分析結果:若是B收到了,那也就意味着只要IP相同,即便port不一樣,NAT也容許UDP包經過。顯然這是Restrict Cone NAT。若是沒收到,沒別的好說,Port Restrict NAT.
這4步都會返回給客戶端它的公網IP、Port和NAT類型,除此以外:
若是A處於公網或者Full Cone Nat下,STUN不作其餘的了,由於其餘客戶端能夠直接和A進行通訊。
若是A處於Restrict Cone或者Port Restrict NAT下,STUN還會協調TURN進行NAT打洞。
若是A處於對稱NAT下,那麼點對點鏈接下,NAT是沒法進行打洞的。因此爲了通訊,只能採起最後的手段了,就是轉成C/S架構了,STUN會協調TURN進行消息轉發。
若是A和B要互相通訊,那麼TURN Server,會命令A和B互相發一條信息,這樣各自的NAT就留下了對方的洞,下次他們就能夠之間進行通訊了。
當A或者B其中一方是對稱NAT時,那麼給這一方發信息,就只能經過TURN Server來轉發了。
假如A、B進行通訊,而B處於對稱NAT之下,那麼A與B通訊,STUN拿到A,B的公網地址和端口號都爲10000,而後去協調TURN打洞,那麼TURN去命令A發信息給B,則A就在NAT打了個B的洞,可是這個B的洞是端口號爲10000的洞,可是下次B若是給A發信息,由於B是對稱NAT,它給每一個新的IP發送信息時,都從新對應一個公網端口,因此給A發送請求多是公網10001端口,可是A只有B的10000端口被打洞過,因此B的請求就被丟棄了。
顯然Server是沒法協調客戶端打洞的,由於協調客戶端打得洞僅僅是上次對端爲Server發送端口的洞,並不適用於另外一個請求。