Intro
我有一个一直以来蛰伏在心里的想法或者说需求,能不能低延迟地与朋友们公用同一台主机,实现即使相隔千里,也能和他们共同观赏一部电影,或者说一起玩一些只支持本地联机的游戏。你可能会说,steam 推出过远程同乐的功能,但是由于高墙的原因,这项服务在国内的稳定性不尽如人意;而类似“一起看电影”的服务,现在也是少之又少,难道我要自己架设一个 HTPC 服务器吗?可这个方案付出的成本对于我这种节省主义者来说过于不可忽视了。直到我在网络工程专业进修了三年,我终于了解到了解决办法:
技术梗概
Zerotier
ZeroTier 使用 SDN(软件定义网络)原理,在IP层面上建立虚拟网络。每个参与的设备都有一个全局唯一的标识——ZeroTier ID,这使得无论物理位置如何,都能像在同一局域网内一样通信。
原理
Zerotier 在多设备之间通过 NAT 打洞的方式来建立 P2P 连接,如:在笔记本电脑、台式机、嵌入式设备、云资源和应用。这些设备只需要通过 ZeroTier One ( ZeroTier 的客户端) 在不同设备之间建立直接连接,即使它们位于 NAT 之后。关于 NAT 打洞可以查看本博客的另一篇文章:NAT 打洞技术

关键词
以下是我们用户在使用 Zerotier 过程中需要关注的几个关键词:
- Planet:行星服务器,Zerotier 根服务器。官方文档如此描述:There is only one planet. Earth's root servers are operated by ZeroTier, Inc. as a free service. 意思是这是由 Zerotier 官方运营的一项免费服务,也就是说这个世界上只有一个 Planet。
- Moon:卫星服务器,用户自建的私有根服务器,起到代理加速的作用。因为大家都懂的原因,Planet 提供的服务不尽稳定,所以我们可以建立自己的 Moon 节点来提供代理服务。
- Leaf:网络客户端,每台连接到网络节点,也就是我们使用的电脑、平板、手机等。
Moonlight
Moonlight(前身为 Limelight)是 NVIDIA GameStream 协议的开源实现,它实现了 NVIDIA Shield 使用的协议,并编写了一组第三方客户端。
在Moonlight - Sunshine 体系下,它是客户端,接收经过编码的信息并解码。
Sunshine
Sunshine 是为 Moonlight 提供游戏串流服务的,可以自己部署的主机服务端 。它提供低延迟的云游戏服务器功能,支持 AMD、Intel 和 Nvidia GPU 的硬件编码(软件编码也可用)。Moonlight 客户端可以连接到部署了 Sunshine 的设备。
也就是说,在 Moonlight - Sunshine 这对兄弟中,Sunshine 是一种作为服务端的存在,它将经过编码的信息推送出去。
搭建
Zerotier
服务端
刚才说过,我们的在服务端的工作就是自建一个 Moon 节点,来代理官方的 Planet 节点。同时,我们还需要一个带有 UI 的控制界面 ztncui。因为最终的连接是 P2P 的,很少需要由 Moon 主动转发数据,所以服务器的要求很低。
前期准备
- 配置服务器的防火墙策略,开放
3443/tcp端口,4000/tcp端口用于 ztncui 的访问,9993/tcp端口,9993/udp端口。 - 服务器安装了 docker,git。
docker 安装 ztucui
docker run --restart=on-failure:3 -d --name ztncui -e \
HTTP_PORT=4000 -e HTTP_ALL_INTERFACES=yes \
-e ZTNCUI_PASSWD=admin@123 \
-p 4000:4000 keynetworks/ztncui
web 的管理页面是 4000 端口(也就是我们放通的端口),我们可以去看看有没有成功安装:通过浏览器访问 ip:4000 进入管理页面。
启动起来后,使用默认的账号 admin 登录进去,密码是 admin@123,登陆后会被要求重置密码。
搭建 Moon 服务器
截至上一步,已经可以创建网络了,但是那使用的还是 Zerotier 的官方服务,我们需要搭建自己的 Moon 服务。
# 创建一个持久化存放文件的目录
mkdir -p /opt/docker/moon
# 获得云服务器的公网ip
public_ip=`curl ip.sb`
# 建立moon服务器
docker run --name zerotier-moon -d -p 9993:9993 -p 9993:9993/udp -v /opt/docker/moon:/var/lib/zerotier-one jonnyan404/zerotier-moon -4 $public_ip
到此我们的服务端就搭建好了。查看一下日志,记录一下 moon id,后面加入会用到。
docker logs -f zerotier-moon
创建一个网络

创建完成后会得到一串神秘代码,记录下它。

回到上一级,通过 easy set up 进行设置。

设置好以后,服务端的配置就告一段落了。
客户端
下载
进入 Zerotier 官网 选择对应系统的版本下载,这里我下载的是 Windows。
完成安装后,加入网络,输入我们在 ztncui 网页上创建的网络的那个神秘代码:

授权
回到 ztncui,对它进行授权(可能需要等一会儿才会显示出来)

加入 Moon
最后在配置一下加入 moon 服务器,打开 cmd。
cd C:\ProgramData\ZeroTier\One
zerotier-cli orbit [moon_id] [moon_id]
将之前记录的 moon id 填进去后执行。

看看有没有成功加入 Moon。
zerotier-cli listpeers

至此已成功加入本机至虚拟网络中,我们可以用同样的步骤让我们的朋友或是自己其他的设备添加进来,记得进行授权喔。
客户端 - 安卓
准备
想要把安卓设备添加到自建的 Moon 节点中需要用到一个第三方的应用,是由一位大神编写的,我们下载并安装到设备。
项目地址:https://github.com/kaaass/ZerotierFix
还需要从我们自己的服务器下载两个文件,如果你是腾讯云,可以使用 OrcaTerm 中文件传输的功能,当然也可以使用 Xftp 等:
*.moon 路径参考图片:

planet 路径参考图片:

下载后把这两个文件也传输给安卓设备,传输完成后具体操作如下:
- 打开安卓设备上安装好的 ZerotierFix
- 右上角设置
- 启动自定义 planet
- 选择从文件导入
- 选择刚才的 palnet 文件,提示导入成功,切换 planet 成功
- 再右上角
- 入轨
- 右下角加号
- 从文件导入
- 选择刚才的 moon 文件
- 提示成功,会在入轨界面看到 moon 服务的 id。
- 回到上一级,右下角加号
- 输入网络 ID 加入网络
- 在 ztncui 上授权
- 完成加入

至此,Zerotier 组网已经完全完成。
Sunshine
项目地址:https://github.com/LizardByte/Sunshine/releases
直接下载安装就行,打开后显示 Web UI,就可以啦!很简单吧。

Moonlight
安卓版本下载地址 https://github.com/moonlight-stream/moonlight-android,有需要也可以下载其他平台版本。
- 安装后,由于此时我们已经在同一个虚拟局域网内,Moonlight 会自动搜索到主机。
- 点击主机,不出意外,在要链接的电脑上会弹出和 Sunshine 配对的 PIN 码,输入 Moonlight 提供的 PIN 码,配对成功。
- 点击 DESKTOP,成功开始串流!

Moonlight 还有很多有意思的功能,也有很高的自定义自由度,比如我可以在平板上使用手写笔,或者新建一个虚拟屏幕。让串流画面输出虚拟屏幕内容,以此来把平板当作一块副屏。这些内容都可以进一步探索。
总结
至此,我们完成了串流环境的部署,在平板使用手机热点、笔记本使用校园网的情况下,采用 1080P@60 fps 的画面,延迟可以稳定在 50 ms 以内,这个数字足以支撑我和朋友们联机游玩一些本地联机的游戏,或者一起看看番剧电影了。话不多说,赶快和你的朋友们一起愉快的玩耍吧!
参考:
https://blog.csdn.net/gitblog_00029/article/details/137257235
https://www.bilibili.com/video/BV1Vh411F7Mr/
https://zhuanlan.zhihu.com/p/522761845