內網穿透-ngrok原理淺析
之前在進行微信Demo開發時曾用到過ngrok這個強大的tunnel(隧道)工具,ngrok在其github官方頁面上的自我詮釋是
“introspected tunnels to localhost",這個詮釋有兩層含義:
1、可以用來建立public到localhost的tunnel,讓居於內網主機上的服務可以暴露給public,俗稱內網穿透。
2、支援對隧道中資料的introspection(內省),支援視覺化的觀察隧道內資料,並replay(重放)相關請求(諸如http請 求)。
因此ngrok可以很便捷的協助進行服務端程式除錯,尤其在進行一些Web server開發中。ngrok更強大的一點是它支援tcp層之上的所有應用協議或者說與應用層協議無關。比如:你可以通過ngrok實現ssh登入到內 網主 機,也可以通過ngrok實現遠端桌面(VNC)方式訪問內網主機。
今天我們就來簡單分析一下這款強大工具的實現原理。ngrok本身是用go語言實現的,需要go 1.1以上版本編譯。ngrok官方程式碼最新版為1.7,作者似乎已經完成了ngrok 2.0版本,但不知為何遲遲不放出最新程式碼。因此這裡我們就以ngrok 1.7版本原始碼作為原理分析的基礎。
一、ngrok tunnel與ngrok部署
網路tunnel(隧道)對多數人都是很”神祕“的概念,tunnel種類很多,沒有標準定義,我瞭解的也不多(日常工作較少涉及),這裡也就不 深入了。在《HTTP權威指南》中有關於HTTP tunnel(http上承載非web流量)和SSL tunnel的說明,但ngrok中的tunnel又與這些有所不同。
ngrok實現了一個tcp之上的端到端的tunnel,兩端的程式在ngrok實現的Tunnel內透明的進行資料互動。
ngrok分為client端(ngrok)和服務端(ngrokd),實際使用中的部署如下:
內網服務程式可以與ngrok client部署在同一主機,也可以部署在內網可達的其他主機上。ngrok和ngrokd會為建立與public client間的專用通道(tunnel)。
二、ngrok開發除錯環境搭建
在學習ngrok程式碼或試驗ngrok功能的時候,我們可能需要搭建一個ngrok的開發除錯環境。ngrok作者在ngrok developer guide
make client和make server執行後,會建構出ngrok和ngrokd的debug版本。如果要得到release版本,請使用make release-client和<span face="Courier
New">make release-server。debug版本與release版本的區別在於debug版本不打包 assets下的資原始檔,執行時通過檔案系統訪問。
修改/etc/hosts檔案,新增兩行:
127.0.0.1 ngrok.me
127.0.0.1 test.ngrok.me
建立客戶端配置檔案debug.yml:
server_addr: ngrok.me:4443
trust_host_root_certs: false
tunnels:
test:
proto:
http: 8080
不過要想讓ngrok與ngrokd順利建立通訊,我們還得製作數字證書(自簽發),原始碼中自帶的證書是無法使用的,證書製作方法可參見《搭建自 己的ngrok服務》一文,相關原理可參考《Go和HTTPS》一文,這裡就不贅述了。
我直接使用的是release版本(放在bin/release下),這樣在執行命令時可以少傳入幾個引數:
啟動服務端:
$> sudo ./bin/release/ngrokd -domain ngrok.me
[05/13/15 17:15:37] [INFO] Listening for public http connections on [::]:80
[05/13/15 17:15:37] [INFO] Listening for public https connections on [::]:443
[05/13/15 17:15:37] [INFO] Listening for control and proxy connections on [::]:4443
啟動客戶端:
$> ./bin/release/ngrok -config=debug.yml -log=ngrok.log -subdomain=test 8080
有了除錯環境,我們就可以通過debug日誌驗證我們的分析了。
ngrok的原始碼結構如下:
drwxr-xr-x 3 tony staff 102 3 31 16:09 cache/
drwxr-xr-x 16 tony staff 544 5 13 17:21 client/
drwxr-xr-x 4 tony staff 136 5 13 15:02 conn/
drwxr-xr-x 3 tony staff 102 3 31 16:09 log/
drwxr-xr-x 4 tony staff 136 3 31 16:09 main/
drwxr-xr-x 5 tony staff 170 5 12 16:17 msg/
drwxr-xr-x 5 tony staff 170 3 31 16:09 proto/
drwxr-xr-x 11 tony staff 374 5 13 17:21 server/
drwxr-xr-x 7 tony staff 238 3 31 16:09 util/
drwxr-xr-x 3 tony staff 102 3 31 16:09 version/
main目錄下的ngrok/和ngrokd/分別是ngrok和ngrokd main包,main函式存放的位置,但這裡僅僅是一個stub。以ngrok為例:
// ngrok/src/ngrok/main/ngrok/ngrok.go
package main
import (
"ngrok/client"
)
func main() {
client.Main()
}
真正的“main”被client包的Main函式實現。
client/和server/目錄分別對應ngrok和ngrokd的主要邏輯,其他目錄(或包)都是一些工具類的實現。
三、第一階段:Control Connection建立
在ngrokd的啟動日誌中我們可以看到這樣一行:
[INFO] Listening for control and proxy connections on [::]:4443
ngrokd在4443埠(預設)監聽control和proxy connection。Control Connection,顧名思義“控制連線”,有些類似於FTP協議的控制連線(不知道ngrok作者在設計協議時是否參考了FTP協議^_^)。該連線 只用於收發控制類訊息。作為客戶端的ngrok啟動後的第一件事就是與ngrokd建立Control Connection,建立過程式列圖如下:
前面提到過,ngrok客戶端的實際entrypoint在ngrok/src/ngrok/client目錄下,包名client,實際入口是 client.Main函式。
//ngrok/src/ngrok/client/main.go
func Main() {
// parse options
// set up logging
// read configuration file
…. …
NewController().Run(config)
}
ngrok採用了MVC模式構架程式碼,這既包括ngrok與ngrokd之間的邏輯處理,也包括ngrok本地web頁面(用於隧道資料的 introspection)的處理。
//ngrok/src/ngrok/client/controller.go
func (ctl *Controller) Run(config *Configuration) {
var model *ClientModel
if ctl.model == nil {
model = ctl.SetupModel(config)
} else {
model = ctl.model.(*ClientModel)
}
// init the model
// init web ui
// init term ui
… …
ctl.Go(ctl.model.Run)
… …
}
我們來繼續看看model.Run都做了些什麼。
//ngrok/src/ngrok/client/model.go
func (c *ClientModel) Run() {
… …
for {
// run the control channel
c.control()
… …
if c.connStatus == mvc.ConnOnline {
wait = 1 * time.Second
}
… …
c.connStatus = mvc.ConnReconnecting
c.update()
}
}
Run函式呼叫c.control來執行Control Connection的主邏輯,並在control connection斷開後,嘗試重連。
c.control是ClientModel的一個method,用來真正建立ngrok到ngrokd的control connection,並完成基於ngrok的鑑權(使用者名稱、密碼配置在配置檔案中)。
//ngrok/src/ngrok/client/model.go
func (c *ClientModel) control() {
… …
var (
ctlConn conn.Conn
err error
)
if c.proxyUrl == "" {
// simple non-proxied case, just connect to the server
ctlConn, err = conn.Dial(c.serverAddr, "ctl", c.tlsConfig)
} else {……}
… …
// authenticate with the server
auth := &msg.Auth{
ClientId: c.id,
OS: runtime.GOOS,
Arch: runtime.GOARCH,
Version: version.Proto,
MmVersion: version.MajorMinor(),
User: c.authToken,
}
if err = msg.WriteMsg(ctlConn, auth); err != nil {
panic(err)
}
// wait for the server to authenticate us
var authResp msg.AuthResp
if err = msg.ReadMsgInto(ctlConn, &authResp); err != nil {
panic(err)
}
… …
c.id = authResp.ClientId
… ..
}
ngrok封裝了connection相關操作,程式碼在ngrok/src/ngrok/conn下面,包名conn。
//ngrok/src/ngrok/conn/conn.go
func Dial(addr, typ string, tlsCfg *tls.Config) (conn *loggedConn, err error) {
var rawConn net.Conn
if rawConn, err = net.Dial("tcp", addr); err != nil {
return
}
conn = wrapConn(rawConn, typ)
conn.Debug("New connection to: %v", rawConn.RemoteAddr())
if tlsCfg != nil {
conn.StartTLS(tlsCfg)
}
return
}
ngrok首先建立一條TCP連線,並基於該連線建立了TLS client:
func (c *loggedConn) StartTLS(tlsCfg *tls.Config) {
c.Conn = tls.Client(c.Conn, tlsCfg)
}
不過此時並未進行TLS的初始化,即handshake。handshake發生在ngrok首次向ngrokd傳送auth訊息(msg.WriteMsg, ngrok/src/ngrok/msg/msg.go)時,go標準庫的TLS相關函式默默的完成這一handshake過程。我們經常遇到的ngrok證書驗證失敗等問題,就發生在該過程中。
在AuthResp中,ngrokd為該Control Connection分配一個ClientID,該ClientID在後續Proxy Connection建立時使用,用於關聯和校驗之用。
前面的邏輯和程式碼都是ngrok客戶端的,現在我們再從ngrokd server端程式碼review一遍Control Connection的建立過程。
ngrokd的程式碼放在ngrok/src/ngrok/server下面,entrypoint如下:
//ngrok/src/ngrok/server/main.go
func Main() {
// parse options
opts = parseArgs()
// init logging
// init tunnel/control registry
… …
// start listeners
listeners = make(map[string]*conn.Listener)
// load tls configuration
tlsConfig, err := LoadTLSConfig(opts.tlsCrt, opts.tlsKey)
if err != nil {
panic(err)
}
// listen for http
// listen for https
… …
// ngrok clients
tunnelListener(opts.tunnelAddr, tlsConfig)
}
ngrokd啟動了三個監聽,其中最後一個tunnelListenner用於監聽ngrok發起的Control Connection或者後續的proxy connection,作者意圖通過一個埠,監聽兩種型別連線,旨在於方便部署。
//ngrok/src/ngrok/server/main.go
func tunnelListener(addr string, tlsConfig *tls.Config) {
// listen for incoming connections
listener, err := conn.Listen(addr, "tun", tlsConfig)
… …
for c := range listener.Conns {
go func(tunnelConn conn.Conn) {
… …
var rawMsg msg.Message
if rawMsg, err = msg.ReadMsg(tunnelConn); err != nil {
tunnelConn.Warn("Failed to read message: %v", err)
tunnelConn.Close()
return
}
… …
switch m := rawMsg.(type) {
case *msg.Auth:
NewControl(tunnelConn, m)
… …
}
}(c)
}
}
從tunnelListener可以看到,當ngrokd在新建立的Control Connection上收到Auth訊息後,ngrokd執行NewControl來處理該Control Connection上的後續事情。
//ngrok/src/ngrok/server/control.go
func NewControl(ctlConn conn.Conn, authMsg *msg.Auth) {
var err error
// create the object
c := &Control{
… …
}
// register the clientid
… …
// register the control
… …
// start the writer first so that
// the following messages get sent
go c.writer()
// Respond to authentication
c.out <- &msg.AuthResp{
Version: version.Proto,
MmVersion: version.MajorMinor(),
ClientId: c.id,
}
// As a performance optimization,
// ask for a proxy connection up front
c.out <- &msg.ReqProxy{}
// manage the connection
go c.manager()
go c.reader()
go c.stopper()
}
在NewControl中,ngrokd返回了AuthResp。到這裡,一條新的Control Connection建立完畢。
我們最後再來看一下Control Connection建立過程時ngrok和ngrokd的輸出日誌,增強一下感性認知:
ngrok Server:
[INFO] [tun:d866234] New connection from 127.0.0.1:59949
[DEBG] [tun:d866234] Waiting to read message
[DEBG] [tun:d866234] Reading message with length: 126
[DEBG] [tun:d866234] Read message {"Type":"Auth",
"Payload":{"Version":"2","MmVersion":"1.7","User":"","Password":"","OS":"darwin","Arch":"amd64","ClientId":""}}
[INFO] [ctl:d866234] Renamed connection tun:d866234
[INFO] [registry] [ctl] Registered control with id ac1d14e0634f243f8a0cc2306bb466af
[DEBG] [ctl:d866234] [ac1d14e0634f243f8a0cc2306bb466af] Writing message: {"Type":"AuthResp","Payload":{"Version":"2","MmVersion":"1.7","ClientId":"ac1d14e0634f243f8a0cc2306bb466af","Error":""}}
Client:
[INFO] (ngrok/log.Info:112) Reading configuration file debug.yml
[INFO] (ngrok/log.(*PrefixLogger).Info:83) [client] Trusting root CAs: [assets/client/tls/ngrokroot.crt]
[INFO] (ngrok/log.(*PrefixLogger).Info:83) [view] [web] Serving web interface on 127.0.0.1:4040
[INFO] (ngrok/log.Info:112) Checking for update
[DEBG] (ngrok/log.(*PrefixLogger).Debug:79) [view] [term] Waiting for update
[DEBG] (ngrok/log.(*PrefixLogger).Debug:79) [ctl:31deb681] New connection to: 127.0.0.1:4443
[DEBG] (ngrok/log.(*PrefixLogger).Debug:79) [ctl:31deb681] Writing message: {"Type":"Auth","Payload":{"Version":"2","MmVersion":"1.7","User":"","Password":"","OS":"darwin","Arch":"amd64","ClientId":""}}
[DEBG] (ngrok/log.(*PrefixLogger).Debug:79) [ctl:31deb681] Waiting to read message
(ngrok/log.(*PrefixLogger).Debug:79) [ctl:31deb681] Reading message with length: 120
(ngrok/log.(*PrefixLogger).Debug:79) [ctl:31deb681] Read message {"Type":"AuthResp","Payload":{"Version":"2","MmVersion":"1.7","ClientId":"ac1d14e0634f243f8a0cc2306bb466af","Error":""}}
[INFO] (ngrok/log.(*PrefixLogger).Info:83) [client] Authenticated with server, client id: ac1d14e0634f243f8a0cc2306bb466af
四、Tunnel Creation
Tunnel Creation是ngrok將配置檔案中的tunnel資訊通過剛剛建立的Control Connection傳輸給 ngrokd,ngrokd登記、啟動相應埠監聽(如果配置了remote_port或多路複用ngrokd預設監聽的http和https埠)並返回相應應答。ngrok和ngrokd之間並未真正建立新連線。
我們回到ngrok的model.go,繼續看ClientModel的control方法。在收到AuthResp後,ngrok還做了如下事情:
//ngrok/src/ngrok/client/model.go
// request tunnels
reqIdToTunnelConfig := make(map[string]*TunnelConfiguration)
for _, config := range c.tunnelConfig {
// create the protocol list to ask for
var protocols []string
for proto, _ := range config.Protocols {
protocols = append(protocols, proto)
}
reqTunnel := &msg.ReqTunnel{
… …
}
// send the tunnel request
if err = msg.WriteMsg(ctlConn, reqTunnel); err != nil {
panic(err)
}
// save request id association so we know which local address
// to proxy to later
reqIdToTunnelConfig[reqTunnel.ReqId] = config
}
// main control loop
for {
var rawMsg msg.Message
switch m := rawMsg.(type) {
… …
case *msg.NewTunnel:
… …
tunnel := mvc.Tunnel{
… …
}
c.tunnels[tunnel.PublicUrl] = tunnel
c.connStatus = mvc.ConnOnline
c.update()
… …
}
}
ngrok將配置的Tunnel資訊逐一以ReqTunnel訊息傳送給ngrokd以註冊登記Tunnel,並在隨後的main control loop中處理ngrokd回送的NewTunnel訊息,完成一些登記索引工作。
ngrokd Server端對tunnel creation的處理是在NewControl的結尾處:
//ngrok/src/ngrok/server/control.go
func NewControl(ctlConn conn.Conn, authMsg *msg.Auth) {
… …
// manage the connection
go c.manager()
… …
}
func (c *Control) manager() {
//… …
for {
select {
case <-reap.C:
… …
case mRaw, ok := <-c.in:
// c.in closes to indicate shutdown
if !ok {
return
}
switch m := mRaw.(type) {
case *msg.ReqTunnel:
c.registerTunnel(m)
.. …
}
}
}
}
Control的manager在收到ngrok發來的ReqTunnel訊息後,呼叫registerTunnel進行處理。
// ngrok/src/ngrok/server/control.go
// Register a new tunnel on this control connection
func (c *Control) registerTunnel(rawTunnelReq *msg.ReqTunnel) {
for _, proto := range strings.Split(rawTunnelReq.Protocol, "+") {
tunnelReq := *rawTunnelReq
tunnelReq.Protocol = proto
c.conn.Debug("Registering new tunnel")
t, err := NewTunnel(&tunnelReq, c)
if err != nil {
c.out <- &msg.NewTunnel{Error: err.Error()}
if len(c.tunnels) == 0 {
c.shutdown.Begin()
}
// we're done
return
}
// add it to the list of tunnels
c.tunnels = append(c.tunnels, t)
// acknowledge success
c.out <- &msg.NewTunnel{
Url: t.url,
Protocol: proto,
ReqId: rawTunnelReq.ReqId,
}
rawTunnelReq.Hostname = strings.Replace(t.url, proto+"://", "", 1)
}
}
Server端建立tunnel的實際工作由NewTunnel完成:
// ngrok/src/ngrok/server/tunnel.go
func NewTunnel(m *msg.ReqTunnel, ctl *Control) (t *Tunnel, err error) {
t = &Tunnel{
… …
}
proto := t.req.Protocol
switch proto {
case "tcp":
bindTcp := func(port int) error {
if t.listener, err = net.ListenTCP("tcp",
&net.TCPAddr{IP: net.ParseIP("0.0.0.0"),
Port: port}); err != nil {
… …
return err
}
// create the url
addr := t.listener.Addr().(*net.TCPAddr)
t.url = fmt.Sprintf("tcp://%s:%d", opts.domain, addr.Port)
// register it
if err = tunnelRegistry.RegisterAndCache(t.url, t);
err != nil {
… …
return err
}
go t.listenTcp(t.listener)
return nil
}
// use the custom remote port you asked for
if t.req.RemotePort != 0 {
bindTcp(int(t.req.RemotePort))
return
}
// try to return to you the same port you had before
cachedUrl := tunnelRegistry.GetCachedRegistration(t)
if cachedUrl != "" {
… …
}
// Bind for TCP connections
bindTcp(0)
return
case "http", "https":
l, ok := listeners[proto]
if !ok {
… …
return
}
if err = registerVhost(t, proto, l.Addr.(*net.TCPAddr).Port);
err != nil {
return
}
default:
err = fmt.Errorf("Protocol %s is not supported", proto)
return
}
… …
metrics.OpenTunnel(t)
return
}
可以看出,NewTunnel區別對待tcp和http/https隧道:
- 對於Tcp隧道,NewTunnel先要看是否配置了remote_port,如果remote_port不為空,則啟動監聽這個 remote_port。否則嘗試從cache裡找出你之前建立tunnel時使用的埠號,如果可用,則監聽這個埠號,否則bindTcp(0),即 隨機選擇一個埠作為該tcp tunnel的remote_port。
- 對於http/https隧道,ngrokd啟動時就預設監聽了80和443,如果ngrok請求建立http/https隧道(目前不支援設定remote_port),則ngrokd通過一種自實現的vhost的機制實現所有http/https請求多路複用到80和443埠上。ngrokd不會新增監聽埠。
從下面例子,我們也可以看出一些端倪。我們將debug.yml改為:
server_addr: ngrok.me:4443
trust_host_root_certs: false
tunnels:
test:
proto:
http: 8080
test1:
proto:
http: 8081
ssh1:
remote_port: 50000
proto:
tcp: 22
ssh2:
proto:
tcp: 22
啟動ngrok:
$./bin/release/ngrok -config=debug.yml -log=ngrok.log start test test1 ssh1 ssh2
Tunnel Status online
Version 1.7/1.7
Forwarding tcp://ngrok.me:50000 -> 127.0.0.1:22
Forwarding tcp://ngrok.me:56297 -> 127.0.0.1:22
Forwarding http://test.ngrok.me -> 127.0.0.1:8080
Forwarding http://test1.ngrok.me -> 127.0.0.1:8081
Web Interface 127.0.0.1:4040
可以看出ngrokd為ssh2隨機挑選了一個埠56297進行了監聽,而兩個http隧道,則都預設使用了80埠。
如果像下面這樣配置會發生什麼呢?
ssh1:
remote_port: 50000
proto:
tcp: 22
ssh2:
remote_port: 50000
proto:
tcp: 22
ngrok啟動會得到錯誤資訊:
Server failed to allocate tunnel: [ctl:5332a293] [a87bd111bcc804508c835714c18a5664] Error binding TCP listener: listen tcp 0.0.0.0:50000: bind: address already in use
客戶端ngrok在ClientModel control方法的main control loop中收到NewTunnel並處理該訊息:
case *msg.NewTunnel:
if m.Error != "" {
… …
}
tunnel := mvc.Tunnel{
PublicUrl: m.Url,
LocalAddr: reqIdToTunnelConfig[m.ReqId].Protocols[m.Protocol],
Protocol: c.protoMap[m.Protocol],
}
c.tunnels[tunnel.PublicUrl] = tunnel
c.connStatus = mvc.ConnOnline
c.Info("Tunnel established at %v", tunnel.PublicUrl)
c.update()
五、Proxy Connection和Private Connection
到目前為止,我們知道了Control Connection:用於ngrok和ngrokd之間傳輸命令;Public Connection:外部發起的,嘗試向內網服務建立的連結。
相關推薦
內網穿透-ngrok原理淺析
之前在進行微信Demo開發時曾用到過ngrok這個強大的tunnel(隧道)工具,ngrok在其github官方頁面上的自我詮釋是 “introspected tunnels to localhost",這個詮釋有兩層含義: 1、可以用來建立public到localho
frp內網穿透的原理
經過一段時間的使用,概況的總結下 內網穿透 讓內網的專案,也可以通過轉發 被外網訪問到 外網地址 +埠 www.ss.com埠預設80 ,即為www.ss.com 通過frp穿透時,內網127.0.0.1+埠/專案名 ,被
內網穿透ngrok的使用教程
我是一個學生,這一次教學寫專案時用到了內網穿透技術,於是我寫了一下,使用教程。 內網穿透一般常用於實現內網釋出的web專案能夠被外網訪問的功能,實現內網穿透的軟體有很多,比如nat123、花生殼或ngrok,這裡我使用ngrok。 實現如下: 我們先進入ngrok.cc
利用ngrok內網穿透 遠端連線手機ssh終端
圖片好大 臥槽 首先手機利用linux deploy安裝了Kali Linux 然後配置好linux的sshd_config 接下來是重點 1.首先在同一個區域網內ssh連線手機中的kali 2.安裝w3m(apt-get install w3m) 3.然後 w3m ngrok.com/d
內網穿透(ngrok伺服器搭建)
轉載:https://blog.csdn.net/zhangguo5/article/details/77848658?utm_source=5ibc.net&utm_medium=referral 簡單來說內網穿透的目的是:讓外網能訪問你本地的應用,例如在外網開啟你本地http://1
內網穿透方法(免費ngrok伺服器搭建)
為什麼要實現內網穿透? 內網計算機(也就是LowID),都通過至少一層閘道器連線網際網路,沒有自己的獨立IP和埠(別人看到的你的IP是閘道器的),所以別人無法主動與你建立連線,兩個內網使用者自然也就無法連通,更無法實現傳輸。但是內網計算機可以主動連線其他有獨立
如何用ngrok進行內網穿透
2.點選左側選單“隧道管理”——>“開通隧道”,進入頁面後點擊購買免費版,進入頁面進行如下操作:“隧道名稱”隨便填入資訊,這裡填入“xxxxxx”;“前置域名”填入xxxxx(如lybwechat);“本地埠”為127.0.0.1:8080(這個可以改,只要與tom
Ngrok內網穿透的幾種利用
其實這篇之前投稿了,不過版權在我這裡,地址在這裡:http://bobao.360.cn/learning/detail/3041.html Ngrok是這樣介紹:一條命令解決的外網訪問內網問題,本地WEB外
ngrok快三原始碼下載內網穿透服務部署記錄
ngrok,【征途原始碼論壇http://zhengtuwangluo.com】聯絡方式:QQ:2747044651一個用於實現內網穿透服務,golang寫的,已經很久遠的一個東西了,可自己部署的版本最後一個版本是1.7.1,很久也沒更新了,但他還是比較穩妥的,
【本人禿頂程式設計師】內網穿透神器:Ngrok在支付中的正確使用姿勢
←←←←←←←←←←←← 我都禿頂了,還不點關注! 前言 隨著網際網路的發展,無論是web服務還是移動APP越來越多的都集成了第三方支付(支付寶、微信、銀聯)。通常作為服務提供方,支付成功以後都會有一個後端回撥URL來通知是否呼叫者是否支付成功,這個URL必須是公網環境,並且可以被訪
一分鐘實現內網穿透(ngrok伺服器搭建)
簡單來說內網穿透的目的是:讓外網能訪問你本地的應用,例如在外網開啟你本地http://127.0.0.1指向的Web站點。 最近公司的花生殼到期了,要續費,發現價格一直在漲,都是5年以上的老使用者,旗艦版都沒有實現內網完全穿透,打算自己動手替換這個服務,中間走
阿里雲搭建ngrok實現內網穿透
內網穿透想必是開發微信的同志所必須的,大部分人首先想到的是去網上找各種現成的吧,比如sunny-ngrok或者向日葵之類的,但是世界上沒有免費的午餐,免費的都是會崩的!!!下面我就來教大家怎麼用阿里雲和ngrok搭建一個內網穿透!!!! 1.準備工作: 要能實現內網穿透,
搭建自己的ngrok服務(內網穿透 使用簡單)
在國內開發微信公眾號、企業號以及做前端開發的朋友想必對ngrok都不陌生吧,就目前來看,ngrok可是最佳的在內網除錯微信服務的tunnel工 具。記得今年春節前,ngrok.com提供的服務還一切正常呢,但春節後似乎就一切不正常了。ngrok.com無法訪問,
阿里雲搭建自己的ngrok服務-實現內網穿透
參考博文:https://blog.csdn.net/qq_34292044/article/details/78559128https://blog.csdn.net/huanxiang201311/article/details/72725891一.環境準備
內網穿透神器ngrok支援linux,windows,mac
原文網址:http://www.phpbulo.com/archives/408.html 買了個樹莓派,平時搗鼓著玩玩,相當於超小型卡片電腦,最近想在上面建立一個web站點使用者訪問,最簡單的方法當然是在路由器上面對映80埠,可是很多人和我一樣沒有路由器的許可權。最近
ngrok域名申請並內網穿透
上一篇文章介紹了[花生殼申請域名以及內網穿透](https://blog.csdn.net/qq_36330228/article/details/85226328),相比花生殼來說ngrok的域名和內網穿透都是免費的,但是ngrok的網路不穩定。下面介紹下如何申請吧: 1.進入Ng
內網穿透工具ngrok的安裝和使用,超簡單~
寫在前面: ngrok可以做內網穿透,外網對映,可以用其他任何有網的裝置訪問你當前開啟對映的埠專案~ 全域性安裝ngrok npm install ngrok -g 如果本地起了一個服務,埠號為3001。則輸入以下命令: ngrok http 3001
一個免費的內網穿透工具-sunny-ngrok
https://www.ngrok.cc/user.html 使用方法:下載安裝======然後去註冊======註冊完成會到個人資訊呢裡=======在個人資訊呢裡找到隧道管理========然後新建隧道====找到那個免費的====然後填寫資訊=====然後會得到一個隧
淺析內網穿透可行方案
緣起 最近在做一個微信網站,需要和微信對接。發現開發的時候需要將自己電腦上的服務對映到公網上,才能接收到微信公眾平臺的回撥。 因此,百度搜尋整理各種可行的內網穿透方案。 內網穿透原理
搭建 ngrok 服務實現內網穿透
文章目錄 提醒:本文最後更新於 1362 天前,文中所描述的資訊可能已發生改變,請謹慎使用。 我們經常會有「把本機開發中的 web 專案給朋友看一下」這種臨時需求,為此專門在 VPS 上部署一遍就有點太浪費了。之前我通常是在 ADSL 路由器上配個埠對映讓本機服務在外網可以訪問,但現在大部