背景
最近作業中有公網訪問內網服務的需求,便了解了內網穿透相關的知識,發現原理和實作都不復雜,遂產生了設計一個內網穿透的想法,
名字想好了,就叫QuantumTunnel,量子隧道,名字來源于量子糾纏現象,
兩個處于量子糾纏的粒子,無論處于多么遠的距離,當其中一個粒子狀態改變時,另外一個粒子也會做出相應的改變,
QuantumTunnel也取意于此,希望把公網發出來的請求,完整的同步到內網,就像在內網發出的請求,打破網路的限制,
什么是內網穿透
什么是內網穿透?摘自百度百科
內網穿透,也即 NAT 穿透,進行 NAT 穿透是為了使具有某一個特定源 IP 地址和源埠號的資料包不被 NAT 設備屏蔽而正確路由到內網主機,
通俗易懂一點就是一個公網內的機器與不能被公網訪問的機器進行資料交換,
這個不能被公網訪問的機器有可能在某個機房,也有可能在家,

典型的應用場景
- 公網訪問內網中的某個系統服務:出于安全等因素考慮,機房一般是不能被公網訪問的;要想訪問機房中的某個服務,就需要用到內網穿透;
- 開發者電腦接收公網回呼:在開發微信業務時,一般會涉及到服務回呼,而開發者電腦無法被公網訪問,這時候也需要內網穿透進行橋接,
設計思路
- 首先,擺在面前的問題是如何讓公網與內網通信,內網的機器不能被公網訪問但是一般都能夠訪問公網(沒有訪問公網能力的場景不在討論范圍內),可以利用這一點,讓內網機器主動與公網的服務器建立雙向通信連接,這樣公網服務器就具備了往內網服務器發送資料的能力;
- 其次,要思考怎么才能讓用戶請求到達這個雙向通信通道,此時就需要一個用戶側的服務器,專門處理來自用戶的請求,并將其轉發到雙向通信通道;
- 上面兩個步驟完成后,用戶請求就來到了內網,此時需要一個客戶端代替用戶進行真正的請求,拿到回傳結果,
歸納總結一下,為了實作內網穿透能力,內網穿透服務中應該有的幾個角色:
用戶服務器:處理用戶過來的請求,將用戶請求轉發給代理服務器;代理服務器:接收用戶服務器過來的請求資料并轉發給往代理客戶端;接收代理客戶端的結果資料并回傳給用戶服務器;代理客戶端:接收代理服務器的請求資料并且進行真正的請求,拿到結果資料后回傳給代理服務器,
架構圖
按照上述思路,內網穿透的架構應該是這樣的:

我們再次梳理一下參與到內網穿透服務的各個角色:
- 用戶客戶端:真實的請求發起方;
- 內網穿透-用戶服務端:接收用戶客戶端發起的請求;并將請求轉發給代理服務端;
- 內網穿透-代理服務端:與代理客戶端保持一個連接通道用于傳輸資料;并且將請求通過該通道傳輸資料到proxy-client;
- 內網穿透-代理客戶端:從通道中接收來自代理服務端的請求資料,并且發起真正的請求,拿到請求結果后再通過該通道寫回到代理服務端;
- 目標服務器:目標服務器,即被代理的服務器,
時序圖
順著這個思路,我們來畫一下時序圖,

時序圖分為兩部分:
-
首先,代理服務器和代理客戶端建立了一條
長連接,用于資料的傳輸,這個很關鍵,
因為代理服務器(公網)無法直接訪問代理客戶端,必須由代理客戶端主動向代理服務器發起連接請求,從而建立一條可以雙向通信的連接通道,利用雙向通信的能力實作代理服務器主動向代理客戶端發送請求的能力 -
然后是一次內網穿透請求的流程,
要注意的是對raw request(response)的處理:raw request -> proxy request ->; raw request,經歷了從原始請求到代理請求再到原始請求的封裝和決議程序,
為什么要有這個協議轉換程序?要實作協議無侵入(如http、ws)的目標,只能在現有協議上擴展請求,如內網穿透http協議,可以把目標地址和目標埠放在header中進行擴展,內網穿透-用戶服務器再把相關的引數給決議出來,從而知道目標地址和埠是什么,這樣處理的好處也顯而易見,在原來的框架、代碼不變的情況下,增加幾個引數就可以用上內網穿透服務,
大家可以了解一下這兩個內網穿透的實作,natx和毒刺,我在設計實作程序中參考了他們的一些實作,
具體實作
結合上面的架構圖、時序圖,要想實作應用層協議無侵入,需要一個能直接在傳輸層進行流量代理的工具,在網路傳輸領域大火的netty便進入了我們的視線,支持TCP、UDP流量轉發,擁有豐富的應用層協議插件,更重要的是發送資料非常方便,只需要往Channel里面寫入資料就行,
對于具體實作,本文暫不討論,計劃放在QuantumTunnel系列博客中的第二篇展開,
下面看看實作的效果,
效果
假設南京本地寶的服務器在內網,我們現在要訪問它的新聞咨詢頻道,原始鏈接為:http://nj.bendibao.com/news/
把目標服務器替換成南京本地寶服務器,重新畫一下架構圖和時序圖:
- 架構圖

- 時序圖

- 訪問結果

從回傳的結果可以看到,通過本地的8090埠,訪問到了南京本地寶的服務器,說明整個鏈路成功走通,
這里解釋一下訪問結果中的幾個引數
proxyHost:被代理的服務器地址;proxyPort:被代理的服務器埠
好了,本篇就聊到這里,后面會推出一個系列博客聊一下基于Netty的實作方案,以及業務隔離、服務高可用的一個探索等好玩的東西,
開源地址
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/350682.html
標籤:Java
