◈ 夜宵吧

中转不该是默认答案:远程控制的三种做法

要在手机上远程控制家里那台电脑,第一反应是「服务器做中转」——它反正一直开着。

但先算一下:手机在国内、电脑也在国内,中间绕一台美西的服务器,两个方向各 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 里处理的那个一样——每个对外开放的服务,都得先想清楚别人拿它能干什么。