前面 15 篇文章,我们已经把 Tunnelto 的核心源码基本拆完了。
从第一篇的完整链路开始,到最后的部署、多实例和可观测性,我们已经分析过这些模块:
#1 从 tunnelto --port 8000 看完整链路
#2 Rust Workspace 架构拆解
#3 客户端启动流程
#4 Wormhole WebSocket 控制通道
#5 ClientHello / ServerHello / ControlPacket 协议设计
#6 远端 HTTP 请求如何转发到 localhost
#7 服务端 Host Header 分发
#8 StreamId 多路复用
#9 控制服务器与 Ping 保活
#10 SecretKey 与 DynamoDB 鉴权
#11 ReconnectToken 断线重连
#12 本地调试面板与 Replay
#13 Docker 自托管部署
#14 Fly.io 多实例扩展
#15 Tracing、Honeycomb 与错误响应设计
这一篇是收尾篇。
我们不再只看某一个源码文件,而是站在产品化和二次开发角度,回答一个更大的问题:
如果基于 Tunnelto 源码,做一个自己的 Ngrok,需要怎么设计?
或者说:
哪些模块可以复用?
哪些模块必须重写?
哪些地方只是开源工具级别?
哪些地方要补成商业化平台能力?
这篇文章不是简单总结,而是给出一个二次开发路线图。
一、先理解 Tunnelto 的本质
Tunnelto 的本质不是“打洞”,也不是传统 P2P 穿透。
它的核心模型是:
客户端主动连接公网服务端
↓
服务端持有一条 WebSocket 控制通道
↓
外部请求进入公网服务端
↓
服务端通过 WebSocket 把请求转发给客户端
↓
客户端再连接本地 localhost
↓
本地响应沿原路返回
也就是说,它是典型的:
反向代理 + 长连接控制通道 + TCP 字节流转发
它和 Ngrok、FRP、LocalTunnel 这类工具在产品形态上很接近,但实现细节有所不同。
如果你想做自己的内网穿透平台,首先要接受这个基本模型:
公网服务端负责接收外部请求
本地客户端负责主动连接公网服务端
中间协议负责把远端 socket 和本地 socket 绑定起来
Tunnelto 已经把这个模型用比较清晰的 Rust 代码实现出来了。
二、Tunnelto 已经提供了哪些核心能力?
从源码看,Tunnelto 已经具备一个内网穿透平台的基础骨架。
1. 客户端 CLI
客户端可以通过:
tunnelto --port 8000
把本地服务暴露出去。
它支持:
指定本地端口
指定本地 host
指定 http / https scheme
指定 subdomain
指定 auth key
启动本地 dashboard
这就是内网穿透产品的用户入口。
2. 服务端控制通道
服务端提供:
/wormhole WebSocket
客户端连接后,服务端执行:
ClientHello / ServerHello 握手
ConnectedClient 登记
Ping 保活
ControlPacket 收发
这部分是整个平台的控制面。
3. 协议库
tunnelto_lib 定义了共享协议:
SecretKey
ClientId
StreamId
ClientHello
ServerHello
ControlPacket
ReconnectToken
客户端和服务端都依赖这个库。
这让协议结构集中管理,避免两端各写一套不一致的类型。
4. Host Header 分发
服务端从远端请求中读取:
Host: abc.tunnelto.dev
提取出:
abc
再查找:
Connections::find_by_host("abc")
这就是 tunnel 子域名路由。
5. StreamId 多路复用
一条 WebSocket 控制通道可以同时承载多个远端请求。
核心靠:
StreamId
ActiveStream
ControlPacket::Init
ControlPacket::Data
ControlPacket::End
ControlPacket::Refused
每个远端 TCP 连接对应一个 StreamId。
这就是并发请求的基础。
6. 本地调试面板
客户端会在本地启动 introspection dashboard。
它可以记录:
请求方法
请求路径
请求头
请求体
响应状态码
响应头
响应体
耗时
并支持 Replay。
这让 Tunnelto 不只是一个转发工具,也有一点 webhook 调试工具的能力。
7. 自托管部署
Tunnelto 提供服务端二进制和 Dockerfile 思路。
服务端分成三类端口:
PORT 远端公网请求入口
CTRL_PORT 客户端 WebSocket 控制入口
NET_PORT 多实例内部通信入口
这让开发者可以部署自己的服务端。
8. 多实例探索
源码中有 network 模块,可以基于 Fly.io Private Networking 做实例发现。
它解决的问题是:
请求进入实例 A,但客户端连接在实例 B 时,如何找到 B?
实现思路是:
查询所有实例
找到持有 host 的实例
把 TCP 流 proxy 到目标实例
这已经有了分布式内网穿透平台的雏形。
三、如果只是自己用,最小改造是什么?
如果你只是想做一个自己用的内网穿透服务,不打算开放给别人,其实改造很少。
可以保留:
客户端 CLI
服务端 control_server
remote.rs
ControlPacket 协议
StreamId 多路复用
本地 dashboard
Docker 部署
重点改几个地方:
1. 改默认 CTRL_HOST,指向自己的服务器
2. 配置自己的 TUNNEL_HOST
3. 配置通配符 DNS
4. 配置反向代理和 TLS
5. 简化或关闭鉴权
6. 单实例部署
最简单的自用架构是:
VPS
↓
tunnelto_server
↓
*.tunnel.example.com
↓
本地 tunnelto 客户端
这个阶段不需要:
用户系统
支付系统
后台管理
复杂多实例
流量计费
限流
自定义域名绑定
团队权限
你只要先跑通核心链路。
四、如果要做成平台,哪些地方必须补?
如果你的目标是做一个类似 Ngrok 的平台,那就完全不同了。
开源代码提供的是技术骨架,但平台化还需要补很多能力。
至少包括:
账号系统
API Key 管理
套餐和计费
子域名保留
自定义域名绑定
访问控制
流量限制
并发连接限制
管理后台
审计日志
多实例调度
高可用
安全防滥用
可观测性
客户端自动更新
这些不是简单改几行代码就能完成的。
所以二次开发时要先明确目标:
做个人工具,还是做 SaaS 平台?
这两个目标的复杂度完全不同。
五、可以复用的模块
如果基于 Tunnelto 开发自己的内网穿透平台,有些模块是非常值得复用的。
1. 协议模型
可以继续复用:
ClientHello
ServerHello
ControlPacket
StreamId
这套协议足够清晰。
它把连接分成两层:
握手层:
ClientHello / ServerHello
转发层:
ControlPacket
这个设计可以保留。
如果后续要扩展,可以增加:
协议版本
错误码
压缩标记
流量统计字段
客户端版本字段
但基础结构不用推翻。
2. StreamId 多路复用
StreamId + ActiveStream 的模型也可以保留。
它解决了核心问题:
一条 WebSocket 如何同时承载多个远端连接?
这个设计简单、直接、可维护。
无论你做个人版还是商业版,都可以继续使用。
3. Host Header 分发
通过 Host Header 做子域名路由,是 HTTP 内网穿透产品最自然的方式。
例如:
user1.yourdomain.com -> client_A
user2.yourdomain.com -> client_B
这部分可以继续保留。
但要增强:
泛域名 DNS
自定义域名
证书管理
保留子域名
非法 Host 防护
4. 本地 dashboard
introspection dashboard 很适合 webhook 调试。
可以继续保留,并增强成产品卖点。
比如:
请求列表
请求详情
JSON 格式化
Replay
搜索过滤
请求收藏
导出 curl
复制请求体
响应预览
这部分很容易做出差异化。
5. Rust + Tokio 异步架构
Tunnelto 基于 Rust 和 Tokio 实现异步网络服务。
这个方向适合内网穿透系统。
因为它需要处理大量:
TCP 连接
WebSocket 长连接
异步读写
通道通信
并发 stream
Rust 的性能和安全性都比较适合这类系统。
六、建议重写或大改的模块
有些模块虽然能跑,但如果要做自己的平台,建议重写或大改。
1. 鉴权系统
Tunnelto 当前鉴权和 DynamoDB、订阅状态、保留域名绑定得比较紧。
如果你要做自己的平台,建议重新设计:
User
ApiKey
Tunnel
ReservedSubdomain
CustomDomain
Plan
Subscription
Usage
不要简单照搬 DynamoDB 三张表。
更通用的关系可以是:
users
api_keys
tunnels
reserved_subdomains
custom_domains
plans
subscriptions
usage_records
如果你熟悉关系型数据库,用 PostgreSQL 会比 DynamoDB 更容易做后台和查询。
2. 错误码系统
当前服务端返回的错误比较简单:
AuthFailed
InvalidSubDomain
SubDomainInUse
Tunnel Not Found
connection refused
如果做平台,建议设计统一错误码:
AUTH_INVALID_KEY
AUTH_PLAN_REQUIRED
DOMAIN_RESERVED
DOMAIN_INVALID
DOMAIN_IN_USE
TUNNEL_NOT_FOUND
LOCAL_CONNECTION_REFUSED
QUOTA_EXCEEDED
RATE_LIMITED
这样客户端可以给出更友好的提示。
3. 多实例发现
当前 network 模块偏 Fly.io 内部网络场景。
如果你的部署环境不是 Fly.io,建议重新设计。
生产级平台可以考虑:
Redis
etcd
Consul
Kubernetes Headless Service
PostgreSQL + heartbeat
NATS
核心是维护:
host -> instance_id
instance_id -> internal_addr
client_id -> instance_id
并配合 TTL 或 heartbeat,避免僵尸记录。
4. 管理后台
Tunnelto 源码里没有完整用户后台。
如果做平台,后台是必须的。
至少需要:
查看 active tunnels
管理 API keys
管理 reserved subdomains
绑定 custom domains
查看请求日志
查看流量用量
查看账单状态
封禁滥用账号
这部分建议单独做一个 Web 后台,不要硬塞进 tunnelto_server 的核心转发进程。
5. 客户端分发和自动更新
开源工具可以让用户 cargo install。
平台产品需要更完整的客户端分发:
macOS Homebrew
Windows 安装包
Linux 二进制
Docker 镜像
自动更新
版本兼容提示
还要考虑:
客户端版本过旧时如何提醒?
协议升级时如何兼容?
不同系统如何保存 auth key?
这些都是产品化必须补的。
七、建议的目标架构
如果要做自己的 Ngrok,可以把系统拆成几个服务。
1. Tunnel Server
负责核心转发:
WebSocket 控制通道
远端 TCP / HTTP 入口
Host 分发
StreamId 多路复用
ActiveStreams
跨实例代理
这是 Tunnelto 当前最核心的部分。
2. API Server
负责平台业务:
用户注册登录
API Key 管理
子域名保留
自定义域名绑定
套餐订阅
用量查询
后台接口
它不应该和转发进程强耦合。
3. Dashboard
负责用户界面:
控制台
隧道列表
请求日志
域名管理
账单页面
团队管理
可以用 Next.js、Vue、React 等实现。
4. Billing Worker
负责计费和异步任务:
同步 Stripe / Paddle / LemonSqueezy
统计用量
生成账单
处理订阅过期
发送邮件通知
5. Metrics / Observability
负责监控:
请求量
带宽
活跃客户端数
stream 数
错误率
节点状态
可以用 Prometheus、Grafana、Loki、OpenTelemetry、Honeycomb 等。
6. Edge Proxy / Load Balancer
负责公网入口:
TLS 终止
泛域名证书
请求转发到 tunnel server
限流
WAF
如果你的 tunnel server 自己处理 TLS,复杂度会更高。
初期可以让 Caddy/Nginx/Traefik/Cloudflare 处理证书。
八、账号系统怎么设计?
Ngrok 类平台的核心不是“能不能转发”,而是“谁有权限使用什么资源”。
建议从这些实体开始:
User
Team
ApiKey
TunnelSession
ReservedSubdomain
CustomDomain
Plan
Subscription
UsageRecord
关系可以这样理解:
User 拥有多个 ApiKey
User 或 Team 拥有多个 ReservedSubdomain
User 或 Team 可以绑定多个 CustomDomain
ApiKey 用于客户端认证
TunnelSession 表示一次在线 tunnel
UsageRecord 记录流量和请求数
Plan 决定限制和权限
Subscription 决定付费状态
客户端启动时:
tunnelto --key xxx --subdomain demo --port 8000
服务端要判断:
key 是否有效?
key 属于哪个 user/team?
demo 是否属于这个 user/team?
当前套餐是否允许自定义 subdomain?
是否超过并发 tunnel 数?
是否超过带宽限制?
账号是否被封禁?
这些都应该在握手阶段完成。
九、API Key 应该怎么设计?
Tunnelto 当前使用 SecretKey。
你可以扩展成更完整的 API Key 系统。
建议:
API Key 只显示一次
数据库只保存 hash
支持 key prefix
支持撤销
支持过期时间
支持权限范围
支持最后使用时间
例如 key 长这样:
tk_live_xxxxxxxxxxxxxxxxx
数据库保存:
key_prefix
key_hash
user_id
created_at
last_used_at
revoked_at
scopes
客户端传 key,服务端 hash 后查库。
不要保存明文 key。
十、子域名系统怎么设计?
内网穿透平台很容易围绕子域名产生产品能力。
可以分三类:
1. 随机子域名
免费用户默认获得:
random-abc.yourdomain.com
优点是简单。
缺点是每次可能变化。
2. 保留子域名
付费用户可以保留:
demo.yourdomain.com
需要数据库记录:
subdomain -> owner_id
并且要防止抢占。
3. 自定义域名
高级用户可以绑定:
webhook.customer.com
这就需要:
DNS 验证
CNAME 配置
证书申请
域名归属校验
TLS 续期
这是平台化难点之一。
十一、域名和证书怎么处理?
如果只是子域名:
*.tunnel.example.com
可以使用通配符证书。
如果支持用户自定义域名,就复杂很多。
你需要处理:
用户添加域名
生成 DNS TXT 验证记录
验证域名归属
申请证书
自动续期
证书存储
TLS SNI 路由
证书吊销
可以选择:
Caddy 自动证书
Traefik
cert-manager
Cloudflare for SaaS
Let's Encrypt ACME
初期建议先只支持平台子域名,后期再做自定义域名。
十二、流量限制怎么做?
平台必须有资源限制。
否则很容易被滥用。
可以限制:
每个账号最大 tunnel 数
每个 tunnel 最大并发 stream 数
每分钟请求数
每日流量
每月流量
最大请求体大小
最大连接时长
空闲超时时间
技术上可以在几个位置做限制:
握手阶段:
限制 tunnel 数和权限
remote.rs:
限制请求数和连接数
process_tcp_stream:
统计入站字节
tunnel_to_stream:
统计出站字节
control_server:
限制客户端连接数
多实例层:
做全局配额同步
流量统计要注意区分:
client -> server
server -> client
remote request bytes
local response bytes
十三、安全防滥用怎么做?
内网穿透平台很容易被滥用。
例如:
钓鱼页面
恶意软件下载
代理非法服务
垃圾流量
暴力扫描
隐藏 C2 服务
所以必须设计安全边界。
至少要有:
账号实名或邮箱验证
API Key 撤销
账号封禁
子域名黑名单
请求速率限制
恶意内容举报
默认错误页
访问日志
滥用检测
黑名单 IP
robots / abuse 联系方式
技术上可以在远端入口增加:
Host 黑名单
Path 黑名单
请求体大小限制
连接超时
空闲超时
IP 限流
账号级限流
平台不是只要能转发就行,还要能控制风险。
十四、客户端要怎么产品化?
Tunnelto 的客户端已经有 CLI 骨架。
但如果做平台,还需要增强客户端体验。
建议增加:
login 命令
logout 命令
token 保存
配置文件
多 profile
版本检查
自动更新提示
友好错误提示
JSON 输出
后台运行模式
配置文件启动多个 tunnel
例如:
mytunnel login
mytunnel http 8000
mytunnel http 8000 --subdomain demo
mytunnel start config.yml
mytunnel status
配置文件可以像这样:
tunnels:
web:
proto: http
addr: localhost:8000
subdomain: demo
api:
proto: http
addr: localhost:3000
subdomain: api-demo
这样用户体验会更接近成熟工具。
十五、协议层怎么扩展?
当前 ControlPacket 很简单:
Init
Data
Refused
End
Ping
如果做平台,可以考虑扩展协议。
例如增加:
ProtocolVersion
ErrorCode
Metrics
WindowUpdate
Compression
ClientInfo
ServerNotice
Drain
GoAway
分别用于:
协议兼容
错误提示
流量统计
背压控制
压缩
客户端版本上报
服务端通知
优雅下线
服务端要求客户端重连
但建议不要一开始过度设计。
可以先加一个协议版本字段。
例如握手时带:
{
"client_version": "1.0.0",
"protocol_version": 1,
"platform": "windows"
}
这样后续升级更稳。
十六、是否要支持 TCP 隧道?
Tunnelto 当前主要围绕 HTTP Host Header 分发。
这非常适合:
Web 服务
Webhook
本地 API
前端调试
OAuth 回调
但如果你要做完整 Ngrok 类平台,用户可能需要 TCP 隧道:
SSH
MySQL
PostgreSQL
Redis
Minecraft Server
自定义 TCP 服务
TCP 隧道不能依赖 HTTP Host Header。
常见做法是:
为每个 TCP tunnel 分配独立公网端口
例如:
tcp://yourdomain.com:32001 -> localhost:22
tcp://yourdomain.com:32002 -> localhost:5432
这需要新增:
端口分配系统
TCP listener 管理
端口权限
端口回收
端口冲突处理
所以建议分阶段:
第一阶段只做 HTTP/HTTPS tunnel
第二阶段再做 TCP tunnel
十七、是否要支持 WebSocket?
实际上,HTTP tunnel 如果是透明字节流转发,一般也能转发 WebSocket upgrade 请求。
因为 WebSocket 握手本质上也是 HTTP 请求升级,后续是 TCP 字节流。
只要服务端不破坏原始请求,客户端也按字节转发,本地服务就可以处理。
但你要注意:
连接时长
空闲超时
Ping/Pong
代理层是否支持 upgrade
反向代理是否配置正确
如果前面用了 Nginx/Caddy,需要保证 WebSocket upgrade 被正确转发。
十八、多实例平台怎么设计?
如果真正做平台,多实例必须重构成更可靠的架构。
建议维护一个全局路由表:
host -> instance_id
client_id -> instance_id
instance_id -> internal_addr
客户端连接成功时:
写入 host -> instance_id
设置 TTL
定期 heartbeat 续期
客户端断开时:
删除 host -> instance_id
远端请求进入任意实例时:
查 host -> instance_id
如果是本机,直接处理
如果是其他实例,proxy 过去
如果不存在,返回 Tunnel Not Found
这个路由表可以存:
Redis
etcd
Consul
PostgreSQL + heartbeat
Redis 是初期比较简单的选择。
十九、如何做优雅下线?
生产环境会滚动发布。
如果一个 tunnel server 要下线,不能直接杀掉。
否则所有连接在它上面的客户端都会断开。
可以设计:
drain mode
流程:
1. 实例进入 draining 状态
2. 不再接受新客户端连接
3. 不再登记新 tunnel
4. 通知已有客户端重连到其他实例
5. 等待活跃 stream 结束
6. 超时后强制关闭
协议层可以增加:
ControlPacket::GoAway
ServerNotice::Reconnect
这样客户端可以平滑重连。
二十、如何做请求日志和审计?
本地 dashboard 适合开发者调试,但平台侧也需要审计日志。
但要注意隐私。
平台不一定应该保存完整请求体。
可以分层:
基础日志:
时间、host、method、path、status、bytes、duration
高级调试:
用户主动开启后,短期保存 header/body
隐私保护:
默认不保存敏感 body
Authorization/Cookie 默认打码
请求日志可以用于:
用户查看访问情况
平台排查问题
用量统计
滥用检测
计费
但一定要在产品条款里说清楚保存范围。
二十一、如何设计管理后台?
后台至少分两类:用户后台和管理员后台。
用户后台
提供:
当前在线 tunnels
历史连接记录
子域名管理
自定义域名管理
API Key 管理
用量统计
账单套餐
请求日志
文档和安装命令
管理员后台
提供:
用户搜索
账号封禁
API Key 撤销
域名封禁
流量排行
错误率排行
节点状态
当前在线连接
滥用举报处理
不要低估后台开发量。
内网穿透平台的核心转发代码可能不长,但后台和运营能力会占很大比例。
二十二、如何做计费?
可以按几种维度计费:
免费版:
随机子域名
低并发
低带宽
不支持自定义域名
请求日志保留时间短
个人版:
保留子域名
更高并发
更多流量
HTTPS
请求日志
团队版:
团队成员
多 API Key
自定义域名
审计日志
更高 SLA
企业版:
私有部署
SSO
专属节点
安全审计
技术上,计费和限流要结合。
否则套餐只是页面上的文字,无法真正控制资源。
二十三、如何做文档和开发者体验?
这类产品非常依赖文档。
用户需要快速知道:
怎么安装客户端?
怎么登录?
怎么暴露本地端口?
怎么固定子域名?
怎么调试 webhook?
怎么绑定自定义域名?
为什么连接失败?
为什么返回 Tunnel Not Found?
建议提供:
Quick Start
CLI Reference
Webhook 调试教程
自定义域名教程
常见错误排查
Docker 部署
CI/CD 使用
API 文档
Tunnelto 源码里的本地 dashboard 和 Replay 是很好的文档卖点。
你可以把产品定位为:
不仅能暴露 localhost,还能调试 webhook。
二十四、第一阶段 MVP 怎么做?
如果你想最快做出一个可用 MVP,可以这样规划。
第 1 周:跑通自托管
部署 tunnelto_server
配置域名
配置通配符 DNS
配置 TLS 反代
客户端能连接
外部能访问 localhost
第 2 周:改造成自己的品牌
改默认 CTRL_HOST
改 TUNNEL_HOST
改 CLI 输出
改 README
改错误页面
改 dashboard 样式
第 3 周:最小账号系统
用户注册
API Key
服务端验证 key
key hash 存储
简单套餐字段
第 4 周:子域名保留
reserved_subdomains 表
用户申请子域名
握手阶段校验归属
后台查看
第 5 周:用量统计
请求数
流量
活跃 tunnel
错误次数
基础 dashboard
第 6 周:上线内测
邀请用户
收集错误
优化 CLI
补文档
加限流
这个 MVP 已经可以验证产品价值。
二十五、不要一开始就做太重
很多人想做自己的 Ngrok,一开始就想做:
多区域节点
全球 Anycast
TCP + HTTP + TLS
自定义域名
团队管理
计费
Kubernetes
高可用
全链路监控
这很容易做不完。
更好的策略是:
先做单区域 HTTP tunnel
先服务 webhook 调试场景
先把客户端体验打磨好
先做简单账号和保留子域名
先跑通付费闭环
因为用户真正关心的是:
能不能稳定访问?
配置是不是简单?
出错时能不能看懂?
Webhook 能不能方便重放?
子域名能不能固定?
先解决这些,比一开始追求复杂架构更重要。
二十六、技术风险清单
如果要二次开发,建议提前关注这些风险。
1. 长连接稳定性
WebSocket 会被代理、NAT、负载均衡影响。
需要:
Ping/Pong
超时
重连
连接清理
优雅下线
2. 内存增长
ACTIVE_STREAMS、请求日志、连接表如果清理不及时,会造成内存泄漏。
3. 滥用风险
开放公网转发后,会有人拿它做违规用途。
必须做封禁和限流。
4. 多实例复杂度
多实例比单实例复杂很多。
先别急着上。
5. TLS 和域名复杂度
自定义域名和证书自动化是一个大坑。
6. 协议兼容
客户端和服务端升级时,要考虑版本兼容。
7. 请求隐私
如果保存请求日志,要处理敏感信息和用户隐私。
二十七、可以形成的产品差异化
如果只是做“另一个 Ngrok”,竞争会比较难。
你可以考虑细分场景。
1. Webhook 调试优先
强化:
请求查看
Replay
签名验证辅助
JSON 格式化
错误对比
回调文档模板
面向支付、GitHub、飞书、企业微信、Stripe 等回调开发。
2. 国内开发者友好
强化:
国内访问速度
中文文档
微信支付/支付宝 webhook 教程
Windows 客户端
一键启动
低价套餐
3. 团队协作
强化:
团队共享 tunnel
权限管理
请求审计
多人查看日志
环境隔离
4. 私有化部署
面向企业:
内网安装
LDAP/SSO
审计日志
专属域名
安全合规
Tunnelto 源码可以作为技术起点,但产品定位要自己设计。
二十八、从源码学习到的核心设计思想
回顾整个系列,Tunnelto 最值得学习的不是某一行代码,而是几个设计思想。
1. 客户端主动连接服务端
避免要求用户配置路由器端口映射。
本地服务不可公网访问
但本地客户端可以主动连公网服务器
这是反向隧道的基础。
2. 控制通道长期存在
通过 WebSocket 保持客户端在线。
一个客户端
一条控制通道
多个远端请求
3. StreamId 做多路复用
远端请求和本地连接通过 StreamId 绑定。
remote socket <-> StreamId <-> local socket
这让并发请求变得可管理。
4. 协议尽量简单
ControlPacket 只有几种核心类型:
Init
Data
Refused
End
Ping
简单协议更容易调试和维护。
5. 服务端按 Host 分发
HTTP tunnel 的核心路由依据是 Host Header。
subdomain -> ConnectedClient
6. 调试能力内置到客户端
本地 dashboard 和 Replay 让产品不只是“能通”,还“好调试”。
7. 运维错误要分层
不同错误对应不同链路阶段:
Invalid Hostname
Tunnel Not Found
Error finding tunnel
Error proxying tunnel
connection refused
这对排查非常重要。
二十九、最终建议:如何开始二次开发?
如果你真的要基于 Tunnelto 做一个自己的内网穿透平台,我建议按这个顺序来:
1. 先完整跑通官方源码
2. 本地单实例自托管
3. 改成自己的域名
4. 改客户端默认控制服务器
5. 去掉或替换 DynamoDB 鉴权
6. 加自己的 API Key 系统
7. 做保留子域名
8. 做用户后台
9. 做请求日志和用量统计
10. 做限流和封禁
11. 做付费套餐
12. 最后再做多实例
不要一开始就重写所有东西。
先复用 Tunnelto 的核心转发链路,把产品跑起来。
然后逐步替换平台层能力。
三十、这一篇的核心结论
Tunnelto 是一个很适合学习和二次开发的内网穿透项目。
它已经实现了:
客户端 CLI
服务端 WebSocket 控制通道
ClientHello / ServerHello 握手
ControlPacket 二进制协议
StreamId 多路复用
Host Header 子域名分发
本地 localhost 转发
本地请求调试面板
Replay
自托管部署
多实例探索
Tracing 和错误响应
如果只是个人自用,可以在它基础上做轻量改造。
如果要做成自己的 Ngrok,则需要重点补齐:
账号系统
API Key
套餐计费
保留子域名
自定义域名
TLS 证书
用量统计
限流
安全防滥用
管理后台
多实例调度
可观测性
客户端分发
一句话总结:
Tunnelto 提供了内网穿透平台的技术骨架,
但真正的 Ngrok 类产品,还需要在账号、域名、计费、安全、运维和用户体验上继续补齐。
从源码学习的角度看,Tunnelto 已经足够完整。
从商业产品的角度看,它只是一个很好的起点。
真正的挑战不是“把 localhost 暴露出去”,而是:
稳定地、可控地、安全地、可计费地、可运维地,
让很多用户同时把自己的本地服务暴露出去。
这就是从 Tunnelto 到自己的 Ngrok,真正要走的路。


416

被折叠的 条评论
为什么被折叠?



