用frp穿透, 体验无公网IP情况下用Moonlight公网游戏串流(附效果视频)

因为一些原因开始使用移动网络上网. 我之前在有宽带的时候在树莓派上装了wakeOnLan唤醒我的台式机, 然后我可以在公司偷偷连接台式机进行远程游戏(一般是FF14钓鱼), 移动网络又没有公网IP, 所以我就想frp穿透的游戏效果不知怎么样, 好奇就开始了……

(English version translate by GPT-3.5)

首先说明

  1. Moonlight选择的码率不能超过服务器转发最大的带宽, 也不能超过路由器最大带宽, 否则卡的飞起.
  2. 我服务器使用5M的带宽, 好像可以串流720p的画质, 但是如果画面变化大的游戏就会模糊(在地平线中可以体现到).
  3. 台式机用5G网络(就是中国移动5G的那个5G, 跟5G频段的路由器没有任何关系), 通过阿里云服务器穿透, 使用iPad进行游戏.
  4. 至于为什么我不选择使用花生壳呢? 我比较喜欢折腾自己感兴趣的…
  5. 这篇只是记录自己折腾过程, 虽然我日常也会用远程串流来挂挂阴阳师啥的, 也可以参考下面这篇, 涉及到串流的也许人家这篇更专业 PC串流真干货——Moonlight篇!

我备的东西

一台云服务器, 要国内的(国外慢死你), 带宽4M或以上, 我用的是阿里云

在服务器上安装frps

从Github上下载

fatedier frp Release 下载页面

frp_0.31.1_linux_amd64.tar.gz 这个是在服务器上运行的

frp_0.31.1_windows_amd64.zip 这个在windows上运行的

安装frp穿透

其实这个安装很简单的, 比ngrok简单的太多.

首先, 下载Linux的包, 然后把它复制到服务器上去

我在/usr/local/frps进行操作, 下载文件后, 使用

1
tar -zxvf frp_0.31.1_linux_amd64.tar.gz

进行解压, 解压得到以下文件

1
2
3
4
5
6
7
8
9
frpc            # Linux版本的frpClient客户端
frpc_full.ini # Linux版本的frpClient客户端完整配置文件
frpc.ini # Linux版本的frpClient客户端精简配置文件
frps # Linux版本的frpClient服务端
frps_full.ini # Linux版本的frpClient服务端完整配置文件
frps.ini # Linux版本的frpClient服务端精简配置文件
LICENSE # 该软件的协议书
systemd(folder) # 这里存着frp客户端和服务端的服务脚本
包含|->frpc.service frpc@.service frps.service frps@.service

其实我们只关注frps和frps.ini就行了, 其他文件用不到.

其次, 修改下frps.ini, 如下

Nvidia Game Stream需要暴露以下端口

1
2
TCP: 47984,47989,48010
UDP: 5353,47998,47999,48000,48002,48010

所以我配置的frps.ini如下

1
2
3
4
5
6
7
8
9
10
11
[common]
# 绑定的端口
bind_port = 9200
# 绑定的网卡IP
bind_addr = 0.0.0.0

# 连接的token
token = 12345678a

# 允许客户端自定义的端口或端口范围, 因为47000-48010包含了上面的端口, 我偷懒而已, 也可以一一定义
allow_ports = 47000-49000,5353

上面绑定的端口指的是客户端和服务端通信端口, 这个端口必须得开放的。绑定的网卡写0.0.0.0表示监听所有网卡的9200端口,如果有多个公网IP的服务器也可以写成指定IP,这样只有那个IP的9200端口可以与服务端通信,而其他的公网IP则无法和9200口通信。这个端口必须开放,不然frpc客户端连不上frps服务端

然后allow_ports是与frpc指定服务的通信端口,和下面配置文件中的remote_port 对应,这些remote_port 定义的端口必须在这里允许。如果要在服务器外访问这些端口需要将其开放。

最后, 启动这个frps, 如果和我一样是阿里云的话, 别忘了将9200口添加到安全组

1
2
3
[root@RuterServer frps]# ./frps -c frps.ini 
2020/01/10 23:42:33 [I] [service.go:152] frps tcp listen on 0.0.0.0:9200
2020/01/10 23:42:33 [I] [root.go:205] start frps success

至于添加到服务, 就麻烦请自行搞定吧

telnet下, 通了 (之前描述成ping, 感谢邱先生的指正)

1
2
3
4
root@RuterPi4:~# telnet ******.com 9200
Trying 47.***.***.190...
Connected to ******.com.
Escape character is '^]'.

安装本机的frp穿透

下载frp_0.31.1_windows_amd64.zip

可以发现和刚才差不多的文件列表, 我直接拿压缩包里面的frpc.exe和frpc.ini

直接贴配置吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
[common]
server_addr = ******.com
server_port = 9200
token = 12345678a

[nvidia-stream-tcp-1]
type = tcp
local_ip = 127.0.0.1
local_port = 47984
remote_port = 47984

[nvidia-stream-tcp-2]
type = tcp
local_ip = 127.0.0.1
local_port = 47989
remote_port = 47989

[nvidia-stream-tcp-3]
type = tcp
local_ip = 127.0.0.1
local_port = 48010
remote_port = 48010

[nvidia-stream-udp-1]
type = udp
local_ip = 127.0.0.1
local_port = 5353
remote_port = 5353

[nvidia-stream-udp-2]
type = udp
local_ip = 127.0.0.1
local_port = 47998
remote_port = 47998

[nvidia-stream-udp-3]
type = udp
local_ip = 127.0.0.1
local_port = 47999
remote_port = 47999

[nvidia-stream-udp-4]
type = udp
local_ip = 127.0.0.1
local_port = 48000
remote_port = 48000

[nvidia-stream-udp-5]
type = udp
local_ip = 127.0.0.1
local_port = 48002
remote_port = 48002

[nvidia-stream-udp-6]
type = udp
local_ip = 127.0.0.1
local_port = 48010
remote_port = 48010

这里面的每一个[nvidia-stream-tcp-1]这样的名字是不能重复的这里稍微

上面的配置意义如下

[nvidia-stream-udp-6] 这是可以理解成一个ID,具有唯一性
type = udp 通信协议类型, 一般有TCP, UDP,这里还有http
local_ip = 127.0.0.1 这个指的是, 在客户端上访问127.0.0.1 这个IP
local_port = 48010 这个指的是, 在客户端上访问48010这个端口
remote_port = 48010 这个指的是, 访问服务端(也就是公网服务器)的48010端口

举个例子,自己有一台公网服务器, IP是64.64.1.222 令名字为PublicHost,然后自己有一台公司的服务器,假设IP是10.1.1.100 取名为InnerHost,这个服务公网是访问不进来的,但是服务器却可以访问公网。然后在公司又放了一台服务器,假设IP是192.168.1.2 取名为PrivateHost,这个PrivateHost服务器它只能和InnerHost通信,端口全通,同时PrivateHost服务器也不能访问公网,PrivateHost上部署了数据库,端口是3306,这里希望能通过PublicHost的13306端口访问到PrivateHost的3306端口,因为PrivateHost只能和InnerHost通信,且InnerHost能访问公网。此时可以在InnerHost上安装Frpc客户端,如下完整配置

[common]
server_addr = 64.64.1.222
server_port = 9200
token = 12345678a

[access-privatehost-db]
type = tcp
local_ip = 192.168.1.2
local_port = 3306
remote_port = 13306

就可以了,上面就是表示,当访问PublicHost的64.64.1.222:13306端口时,就等同于在InnerHost里面访问192.168.1.2:3306端口,此时就可以在公网通过PublicHost访问这台PrivateHost的数据库了

运行frpc.exe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
C:\Users\****\Desktop\frpc>frpc.exe -c frpc.ini
2020/01/10 23:56:29 [I] [service.go:250] [45be81afc5d21f1d] login to server success, get run id [45be81afc5d21f1d], server udp port [0]
2020/01/10 23:56:29 [I] [proxy_manager.go:144] [45be81afc5d21f1d] proxy added: [nvidia-stream-tcp-1 nvidia-stream-udp-1 nvidia-stream-udp-2 nvidia-stream-udp-3 nvidia-stream-udp-4 nvidia-stream-udp-6 nvidia-stream-tcp-2 nvidia-stream-tcp-3 nvidia-stream-udp-5]
2020/01/10 23:56:29 [I] [control.go:164] [45be81afc5d21f1d] [nvidia-stream-tcp-1] start proxy success
2020/01/10 23:56:29 [I] [control.go:164] [45be81afc5d21f1d] [nvidia-stream-udp-1] start proxy success
2020/01/10 23:56:29 [I] [control.go:164] [45be81afc5d21f1d] [nvidia-stream-udp-2] start proxy success
2020/01/10 23:56:29 [I] [control.go:164] [45be81afc5d21f1d] [nvidia-stream-udp-3] start proxy success
2020/01/10 23:56:29 [I] [control.go:164] [45be81afc5d21f1d] [nvidia-stream-udp-4] start proxy success
2020/01/10 23:56:29 [I] [control.go:164] [45be81afc5d21f1d] [nvidia-stream-udp-6] start proxy success
2020/01/10 23:56:29 [I] [control.go:164] [45be81afc5d21f1d] [nvidia-stream-tcp-2] start proxy success
2020/01/10 23:56:29 [I] [control.go:164] [45be81afc5d21f1d] [nvidia-stream-tcp-3] start proxy success
2020/01/10 23:56:29 [I] [control.go:164] [45be81afc5d21f1d] [nvidia-stream-udp-5] start proxy success
2020/01/10 23:56:29 [I] [proxy.go:438] [45be81afc5d21f1d] [nvidia-stream-udp-3] incoming a new work connection for udp proxy, 47.***.***.190:9200
2020/01/10 23:56:29 [I] [proxy.go:438] [45be81afc5d21f1d] [nvidia-stream-udp-1] incoming a new work connection for udp proxy, 47.***.***.190:9200
2020/01/10 23:56:29 [I] [proxy.go:438] [45be81afc5d21f1d] [nvidia-stream-udp-2] incoming a new work connection for udp proxy, 47.***.***.190:9200
2020/01/10 23:56:29 [I] [proxy.go:438] [45be81afc5d21f1d] [nvidia-stream-udp-4] incoming a new work connection for udp proxy, 47.***.***.190:9200
2020/01/10 23:56:29 [I] [proxy.go:438] [45be81afc5d21f1d] [nvidia-stream-udp-5] incoming a new work connection for udp proxy, 47.***.***.190:9200
2020/01/10 23:56:29 [I] [proxy.go:438] [45be81afc5d21f1d] [nvidia-stream-udp-6] incoming a new work connection for udp proxy, 47.***.***.190:9200

至此, frpc的部分完成了, 如果想测试下穿透是否能用了, 对于TCP自行telnet, 对于UDP可以使用dig查DNS来检查是否通(frp作者的方法)

检查端口是否全通

  1. 首先我们先将所有的type = udp的local_ip改为223.5.5.5, local_port改为53, 然后重启frpc, 就像下面一样修改, 因为DNS是运行在53端口上, 使用UDP进行通信的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
      [nvidia-stream-udp-1]
    type = udp
    local_ip = 223.5.5.5
    local_port = 53
    remote_port = 5353

    [nvidia-stream-udp-2]
    type = udp
    local_ip = 223.5.5.5
    local_port = 53
    remote_port = 47998
    .......
    [nvidia-stream-udp-6]
    type = udp
    local_ip = 223.5.5.5
    local_port = 53
    remote_port = 48010
  2. 重启后, 使用任何一台公网服务器通过dig进行测试下, 输出结果如下(所有的结果都输出了类似如最后一个结果的)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    root@RuterPi4:~# dig @******.com -p 5353 www.baidu.com
    root@RuterPi4:~# dig @******.com -p 47998 www.baidu.com
    root@RuterPi4:~# dig @******.com -p 47999 www.baidu.com
    root@RuterPi4:~# dig @******.com -p 48000 www.baidu.com
    root@RuterPi4:~# dig @******.com -p 48002 www.baidu.com
    root@RuterPi4:~# dig @******.com -p 48010 www.baidu.com
    ; <<>> DiG 9.11.5-P4-5.1-Raspbian <<>> @******.com -p 48010 www.baidu.com
    .........
    ;; ANSWER SECTION:
    www.baidu.com. 43 IN CNAME www.a.shifen.com.
    www.a.shifen.com. 43 IN A 112.80.248.76
    www.a.shifen.com. 43 IN A 112.80.248.75
    ;; Query time: 47 msec
    .......

    当然TCP端口也确保通了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    root@RuterPi4:~# telnet ******.com 47984
    Trying 47.***.***.190...
    Connected to ******.com.
    ...

    root@RuterPi4:~# telnet ******.com 47989
    Trying 47.***.***.190...
    Connected to ******.com.
    ...

    root@RuterPi4:~# telnet ******.com 48010
    Trying 47.***.***.190...
    Connected to ******.com.
    ...
  3. 将配置改回来吧, 到这里其实远程串流已经弄好了

开始串流

下好Moonlight, 开始试玩地平线了

我这里使用iPad来体验下, 关闭WIFI, 使用移动网络(或手机热点, 其实我只是想展示下我没有用局域网罢了)

now-using-mobile-data

添加服务器的IP

add-server-ip

因为我们已经做了穿透, 且穿透的端口就是GameStream的原端口, 所以Moonlight访问服务器的这几个端口时, 实际就是在访问我们本机的这些端口了

如果没有出意外, 那么会看到一个锁住的电脑, 那就成功了

locked-pc

当然如果没有看到, 就尝试一下以下步骤

  1. 再次改成dns的53端口, IP改为223.5.5.5, 然后dig再次确认下
  2. 与PC在同一个局域网下测试串流是否能用, 确保Nvidia GameStream服务已经正常开启, 必要的话检查下防火墙啥的

连接台式机

点击锁住的电脑, 会要求进行配对操作, 此时Pad端显示如下

ipad-pc-pair-ipad-side

而PC端右下角会弹出这样的框框

ipad-pc-pair-pc-side

在电脑端输入相同的数字后, 即可完成配对(配对只需1次, 之后就不用配对了)

pair-success

试玩极限竞速:地平线4(附试玩视频)

别忘了设置下参数, 不然卡死(因为我的服务器最大带宽5Mbps)

param-setting

ok了, 丢脸的放一个录屏, 还请别吐槽我手残党操作, 只供效果预览就行了(视频中2:57卡了下, 好像是电脑卡住了, 而不是网络卡住了)

原始码率视频下载 百度云盘 OneDrive
百度云盘提取码: iz4c
OneDrive访问密码: Eh$Hlw=BaKC_#~@W)Rifm8V*

还有就是注意, 在游戏期间,手机其实是处于长时间的下载的状态, 你玩1秒钟, 就要耗费500KB(约)的流量, 我体验了12分钟, 就消耗了820MB的流量, 从阿里云的入网出网记录看, 这段时间宽带基本是满的.

all-of-data's

个人感觉

我觉得吧…

  1. 公网穿透嘛, 除非有自己的公网IP(例如家里的宽带, 一般有公网IP), 或穿透带宽足够, 否则效果不是特别理想的(尤其对于画质党而言).
  2. 玩手柄游戏其实感觉还是可以的, 延迟有但是可以接受, 但是如果玩键鼠游戏, 那么延迟感觉就非常严重了, 有一种明明拖过去了但是没有拖过去的错觉.
  3. 卡顿感还是偶尔的, 毕竟网络摆在那里, 我还是用来挂阴阳师或偶尔FF14上班休息的时候钓钓鱼吧.