中转不该是默认答案:远程控制的三种做法
要在手机上远程控制家里那台电脑,第一反应是「服务器做中转」——它反正一直开着。
但先算一下:手机在国内、电脑也在国内,中间绕一台美西的服务器,两个方向各 158 毫秒,光是输入延迟就三百多毫秒。这个方案在最糟的位置上花了最多的力气。
本文对比三种做法,说明为什么最后选了「服务器只做信令、两端自己打洞」,以及打洞失败之后该怎么兜底。
先算一下绕路的代价
链路是这样的:手机在国内,服务器在大洋另一边,电脑在国内。
手机 → 服务器 → 电脑
单程 158 ms,往返 316 ms。而远程桌面的输入延迟大致等于一个往返——拖动窗口时,画面要比手指慢三分之一秒。这个量级已经不能叫「稍微有点延迟」,是明显的不跟手。
中转的价值在于「两端都能出网就能连上」。但它付出的代价是:所有像素都飞一趟国外再飞回来。
中转应该是兜底,不是默认。
三个方案
| 方案 | 延迟 | 前提 | 代价 |
|---|---|---|---|
| 系统自带远程桌面 + 端口映射 | 最低 | 有公网地址 | 家里大概率没有 |
| 第三方远控(走厂家服务器) | 高 | 无 | 画面经过别人的机器 |
| 自建信令 + P2P 打洞 | 最低 | 两端能出网 | 打洞失败时仍要中转 |
第三条是想要的形状:服务器只负责让两端互相认识,画面走两端之间的直连。
选了开源自建方案,它的架构很简单,两个组件:
hbbs:会合服务,负责登记和牵线hbbr:中继服务,打洞失败时才用
两端都是主动出站去连会合服务,所以家里那台机器有没有公网地址都无所谓——不需要端口映射,不需要固定地址。
打洞失败之前先别假设它会失败
我原本担心运营商级 NAT 会让打洞大概率失败,那就退化成全程中转、白折腾。
实测结果比预期好:两端成功直连,中继日志里那个地址出现 0 次。
判断方法很直接——看中继的日志有没有出现过客户端的地址。没有,就说明画面没经过它。
这也说明一件事:「NAT 类型差就打不通」是个概率判断,不是结论。真正该做的是先试,把中继当兜底,而不是一开始就选中继。
中继不能对着所有人开
装完第二天翻日志,看到凌晨有两条连接:
69.x.x.100 0.3s 断开
69.x.x.27 0.3s 断开
来源是某云服务商,两次都只连了 0.3 秒。
原因:中继组件默认允许匿名使用。谁知道了地址,都能拿它当免费中转。三秒探测说明是被扫描器扫到的。
修法给中继加一个共享密钥,只有持同一密钥的客户端才能用:
hbbr -k <共享密钥>
加了之后,客户端那边不用改任何配置——密钥是服务端之间对暗号用的。扫描器再连过来,握手就失败。
这类「默认可匿名使用」的设计在自建工具里很常见,装完就该先问一句:这个东西如果被别人发现,能用来干什么。
流量才是成本
有人问中继对带宽要求高不高。
瞬时带宽其实不是问题,办公场景 1~3 Mbps 就够,服务器端口是千兆。真正要算的是流量:
| 场景 | 码率 | 每天 2 小时,月流量 |
|---|---|---|
| 看文档、改代码 | 2 Mbps | 约 53 GB |
| 看视频 | 8 Mbps | 约 211 GB |
| 玩游戏 | 15 Mbps | 约 396 GB |
而打洞成功时这部分流量是 0——只走了几 KB 的信令。
所以这个方案的成本模型很有意思:用得越顺,服务器越轻松。只有打洞失败时才真正开始烧流量。
什么时候不该这么做
两端 NAT 都很严时。 打洞依赖至少一端能接受入站。两端都在运营商大内网里时成功率会掉,这种情况下中转是常态而不是例外。
需要长时间稳定会话时。 打洞靠的是 NAT 映射项的存活期,某些家用路由器只维持几十秒,会话会被打断重连。对远程桌面这种长连接,一次重连就是画面卡两秒。
手机在移动网络下。 蜂窝网出口地址变化频繁,换基站就可能要重新打洞。
三条都指向同一个结论:直连是优化项,不是架构前提。 所以中转必须始终可用。
结果
- 两端直连,画面不经过中转
- 服务器只承担信令,内存占用十几兆
- 中继加了密钥,扫描器扫到也用不了
回到开头那句判断:中转不该是默认答案。它真正的位置是「试过直连但失败」之后的那一步。
这套东西的暴露面和 harden 里处理的那个一样——每个对外开放的服务,都得先想清楚别人拿它能干什么。