內網穿透原理及實現一:C/S,P2P模式原理


1.前言

最近一陣子在研究內網穿透,查了不少資料,所以今天就聊聊兩種不同的穿透方式的原理,以及基於java的netty框架的實現,代碼也已在我的github。

起因:突然花這么大力氣研究這個雖然是頭腦發熱所為,但動機源於跟小伙伴聯機打游戲,原來用nat123之類的做遠程端口轉發在最近一陣子巨慢無比,又沒找好的替代方案,前一陣子公司同事又分享過nio,網絡編程的知識又被過了遍,一拍腦子自己造輪子得了,正好自己又有個阿里雲可以當中間節點,找了些參考文章就開搞了,陸陸續續摸索了三個周末,基本可以用了,這兩篇文章就當總結了。

2.問題由來

在一般的網絡應用中,簡單拓撲模型如下

server有公網ip,server應用直接監聽公網ip及端口,client訪問可直接通過ip:port 直接建立鏈接,進行請求響應。

但如果服務端通過路由鏈接Internet,沒有直接暴露公網ip就不同了,如以下拓撲

server在內網,沒有公網ip,路由有公網ip,通過路由鏈接Internet,client請求抵達server路由后,除非在路由定義端口轉發規則,否則數據將被拋棄,但很多時候路由的轉發規則是無法被改的。

這樣的模型可以細分為四種情況

  1. client有公網ip,server在內網;
  2. client在內網,server在公網;
  3. client,server均在公網;
  4. client,server均在內網。

對於server在公網的情況,client可以主動鏈接;但server在內網時,我們便需要內網穿透了。接下文章將以最復雜的第4種情況進行分析。

3.內網穿透原理

3.1 基於TCP的C/S模式

對於client和server均在內網的情況,直連是不行的,但如果我們有另外一台具有公網ip的服務器充當中間節點,便可以進行間接訪問了,拓撲如下

中間服務器我們暫稱為forwarder
我們詳細描述下請求響應過程

  1. client由本機777端口通過路由向forward 11.11.11.11:666發起請求,路由分配公網ip及端口12.12.12.12:45464給client,這里的端口是隨機分配的,也就內網機器可以訪問Internet應用的原因。
  2. client連上forwarder后,對應forwarder來說,它獲取到的客戶端是12.12.12.12:45464,所有client的777端口的請求都會由路由的45464端口發送給forwarder,同時Forwarder對client的響應其實是發往路由的45464端口,再由路由轉發給client的777端口;
  3. server的80端口經它的路由13.13.13.13:12454也同樣與forwarder建立連接;
  4. 這時forwarder只需要將client發送請求數據轉發給server的路由12454端口,然后server的響應也經server的路由,發給forwarder,forwarder再轉發給client的路由,最后發送至client的777端口,這樣就完成了一次穿透兩個內網的請求與響應;
  5. 重復過程4,內網穿透也就成功了。

我們可以看到內網機器能訪問Internet的原因在於它的路由為其分配了一個隨機的公網端口。這里順帶解釋概念 端口映射,此時的公網端口12.12.12.12:45464邏輯上可以認為就是內網機器777端口的映射。再抽象一點,對於client來說,forwarder的666端口其實也可以認為是server 80端口的映射,這個便是遠程端口映射,因此,遠程端口映射跟內網穿透,描述雖不同本質上是一致的。

這種模式非常簡單直接,只要client和server能連上Internet,就能穿透彼此的內網相互訪問。
諸如nat123的端口轉發機制,穿透內網的遠控軟件TeamViewer,我們玩一些對戰游戲的平台,甚至各種網絡游戲都可以看成這種模型的實現。

它的優點在於forwarder服務器存在,可以協助穿透任何形式的內網,簡單穩定,但同樣由於存在中間服務器,網絡上不僅受客戶端網絡限制,同樣也受forwarder的網絡限制,如果forwarder帶寬不行,或者傳輸幾g的大文件,效率就慢了。為了解決這個問題,基於P2P的機制也就提出來了。

3.2基於UDP的P2P模式

先回頭看下C/S模式的網絡拓撲

C跟S最終還是由各自路由隨機分配的公網端口進行Internet訪問,這樣的話,如果它們能彼此知道對方的公網ip和端口,比如經forwarder將ip端口發給對方,是不是就可以直接TCP實現P2P訪問呢,答案是比較困難的。

原因在於TCP是一種先連接后傳輸的通信協議,分配給client的45464只能與forwarder的666端口傳輸數據,這是在連接建立時確定的,如果client再想鏈接server的路由,此時的端口將是重新分配的。並且因為安全等問題考慮,端口是隨機分配,無法固定。也有一些資料說可以通過某些算法猜測到公網端口從而實現基於TCP的P2P傳輸,但實現起來比較困難。我們這里就通過UDP來實現,因為它跟TCP不同,是在發送數據時指定目標ip端口。

下面來看看基於UDP的P2P模式的網絡拓撲

以下是請求響應過程

  1. client開始向forwarder發送請求,由於使用udp,我們不需要建立連接,路由分配ip,端口后,數據包直接發往forwarder;
  2. forwarder由此可以得到client的公網ip及端口12.12.12.12:45464;
  3. 同樣的forwarder得到server的公網ip及端口13.13.13.13:12454;
  4. forwarder將包含server的ip:端口的數據包發給client,將client的發給server;
  5. 由於是UDP,此時client可以直接將包含請求的數據包改為得到的server地址,直接發往server路由,server同樣可以直接發給client了。
  6. 雙方接受到對方數據后,可以認為一個虛擬的P2P連接就已經建立了, 至此內網穿透便實現了。

P2P方案優點也就明顯的,數據傳輸不依賴於中間服務器,在連接建立后就不再受其限制,但同樣由於UDP的特性,數據可靠性難以保證,所以得容忍誤差,或者實現一些校驗機制,而且對於Symmetric NAT,P2P是無法建立的,還是只能走C/S模式的穿透。

它的應用最廣泛的就是各種BT客戶端了,畢竟大數據的傳輸不需要經過中間服務器,效率會高很多,然后一些P2P的聊天,視頻,游戲對戰軟件也可以用到。

至此,兩種實現模式的原理已經說明了,下一篇將講講基於java netty框架造的兩個輪子。

-源碼 https://github.com/chulung/forwarder

參考文檔

內網穿透並不是一個新話題,已經有很多成熟的協議和框架實現了,比如這個,但學習,還是自己造輪子得好。

NAT穿透解決方案介紹

作者:初龍

原文鏈接:https://chulung.com/article/intranet-penetration-principle-and-implementation-one-cs-p2p-mode-principle

本文由MetaCLBlog於2017-07-20 09:00:00自動同步至cnblogs

本文基於 知識共享-署名-非商業性使用-禁止演繹 4.0 國際許可協議發布,轉載必須保留署名及鏈接。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM