两个局域网的主机要进行远程桌面,必须进行内网穿透。frp 是一个不错的选项。但其实不依赖外部的工具,仅仅利用系统的 SSH 也能实现,而且在安全性上可能会更好。
最终架构
参与该网络的各个机器、端口、执行的命令及其顺序如下图所示:
实现步骤
假设我们现在在办公室有一台 MacOS 主机 C,要连接家里的 MacOS 主机 A。
- 购买一台公网服务器 B,且具有公网 IP。带宽可能要的比较大,见下文的说明。假设公网 IP 为 123.1.2.3,其 SSHD 端口为 22。
- 在主机 A 上,开启 VNC 服务:前往 System Preferences->Share,勾选 Screen Share。它会监听本机的 5900 端口。
- 在主机 A 上,开启 SSHD 服务:前往 System Preferences->Share,勾选 Remote Login。它会监听本机的 22 端口。
- 在主机 A 上,开启 SSH 反向隧道:
ssh -gNTR 127.0.0.1:7000:127.0.0.1:22 root@123.1.2.3
。这里相当于在公网服务器 B 上开了一个新的 SSHD 服务代理,其端口是 7000(这里是示例端口,可以自己修改,下同)。 - 在公网服务器 B 上,通过 7000 端口代理的 SSHD 服务,建立一个到主机 A 的 VNC 服务的反向代理:
ssh -p 7000 -N -L 127.0.0.1:5900:127.0.0.1:5900 admin@127.0.0.1
(admin 表示主机 A 的登录用户名,请改成自己使用的)。这里就相当于公网服务器 B 提供了一个 VNC 服务,其端口为 5900。 - 在主机 C 上,建立一个到公网服务器 B 的 VNC 服务的反向代理:
ssh -N -L 127.0.0.1:5900:127.0.0.1:5900 root@123.1.2.3
。这里就相当于主机 C 提供了一个 VNC 服务,其端口为 5900。 - 在主机 C 上,打开 Finder->Go->Connect to Server,填写
vnc://127.0.0.1
,然后在弹出的用户密码框中输入远程主机的登录用户名(如示例的 admin)及其密码,就可以访问咯。或者用命令行打开:open vnc://127.0.0.1:5900
当用户进行远程桌面时,比如移动一下鼠标,此时其请求数据流向为 C:127.0.0.1:5900 -> B:123.1.2.3:22 -> B:127.0.0.1:5900 -> B:127.0.0.1:7000 -> B:123.1.2.3:22 -> A:127.0.0.1:22 -> A:0.0.0.0:5900。
效果
以上所有的端口都是本地 127.0.0.1 的,除了公网服务器 B 需要对外暴露 SSHD 端口(22),无需再暴露任何其他主机、其他端口,而且使用 OpenSSH 加密通信数据,相对来说比较安全。当然上面用了公网服务器的 root 用户,你也可以新建一个低权限用户来做这个,进一步提升安全性。
但是也存在一些需要关注的问题、优化的地方。最大的一个问题就是卡顿。在 1Mbit/s(其实只有 100KB)的公网带宽服务器上,效果一般。打字什么的没什么问题。要是传送文件,挤占带宽后会导致屏幕完全没法操作。浏览网页比较卡顿,可以看到明显的延迟(尤其是滚动页面时)。这一点相比 TeamViewer 免费版来说都差很多。很大的原因可能还是 VNC 的协议不如 TeamViewer,OpenSSL 的加密也比较重。公网带宽提升到 10Mbit/s 可能会比较流畅,当然还要确保家里的电脑的上传带宽也足够(>=10Mbit/s),可以用 http://www.speedtest.cn/ 测试下网速。
其他的一些问题:
- SSH 有时候不太稳定,容易断掉。可以考虑使用 autossh 之类。
- 输入文字时会有一个大的光标,可能是协议实现的问题。
- 使用 MacOS 自带的 VNC 客户端,复制文件只能通过拖拽方式,无法直接 CMD+C、CMD+V。但复制文字可以用快捷键。
- 如果远程主机配置了屏保,有时候取消屏保特别慢。这个时候可以先断开连接,重新连。可能还是带宽不足的并发症。
这个方式一般来说作为 TeamViewer 免费版的一个备份还可以。有时候 TeamViewer 在电脑屏保或锁屏或最小化时,无法连接上。可以通过本方法先登录远程主机来排查解决。Windows 及其 RDP 协议的也差不多类似,只是把上面 VNC 的部分替换成 RDP,使用 Microsoft Remote Desktop 等客户端来连接即可。