MENU

【歪门邪道】想办法让朋友连上我家里的Minecraft服务器

October 1, 2024 • 瞎折腾

最近和朋友联机 Minecraft,用的 Tailscale,但有时对方需要使用蜂窝网络,这个时候 Tailscale 的穿墙打洞就不太行了。怎么办呢?开个国外的服务器便宜配置高,但是延迟大;开个国内的服务器网络好但是配置贵。我灵机一动:开个服务器做流量转发吧!

因为服务器只有两个人联机,所以不需要 24 小时在线运行,我这边的策略是在我的 NUC 上运行 Minecraft,然后我来开局域网共享。在 Tailscale 能穿墙打洞的情况下,我们可以通过家宽的 IPv6 直连,所以延迟很低。但有时对方需要在单位值班,这个时候就要自己的手机开热点给电脑用,而这种情况下 Tailscale 无法穿墙打洞,这是因为运营商对于蜂窝网络使用的是一种 NAT,这种情况下 Tailscale 会走国外的中继,这样一来不光延迟大增,有时候还会丢包。为了解决这个问题,我决定租用一台云服务器。

上云?家境贫寒,告辞!

一开始原本打算把服务器搬到云上,但最主要的问题无外乎成本和延迟,后者也可以归进成本。由于我使用国外服务器比较多,因此对国外的价格比较熟悉。像是我常用的云服务供应商 Hetzner,他们家一台 8 核心 16GB 内存的机器大约是这个价格:

  • 共享 Intel 机型 CX42:每月 20 欧元(不到 160 元人民币)
  • 共享 AMD 机型 CPX41:每月 30 欧元(不到 240 元人民币)
  • 共享 Ampere 机型 CAX31:每月 15 欧元(不到 120 元人民币)
  • 独享机型 CCX33:8 核 32GB 内存每月 60 欧元(不到 480 元人民币)

这些价格还包含了至少为 160GB 的 NVMe 硬盘、万兆共享网络接口、20TB 上传以及下载不计费。

对应地,阿里云的服务器(北京区,以下价格为 1 个月包月价格,按量付费会更贵):

  • Intel 系列最便宜的 ecs.e-c1m2.xlarge:每月 216 元
  • AMD 系列最便宜的 ecs.c8a.xlarge:每月 371.71 元
  • Arm 系列最便宜的 ecs.c8y.xlarge:每月 256.26 元

阿里云的价格只是配置费,不包含硬盘和按流量计费的百兆共享网络。

再来看看腾讯云(同样北京区,1 个月包月价格):

  • Intel 系列最便宜的 S5.LARGE8:每月 288.14 元
  • AMD 系列最便宜的 SA3.LARGE8:每月 241.6 元
  • Arm 系列最便宜的 SR1.LARGE8:每月 262.8 元

同样,这个价格只有配置费,不包含硬盘和按流量计费的宽带。

我一直以来使用 Hetzner 的服务器就是因为便宜量大还管饱,但唯一不好的一点就是网络延迟很高。Minecraft 作为一个需要操作的游戏,它对于延迟还是很敏感的。一般人的情况下(高 ping 战士不在此列),ping 值在 100 毫秒以下就算是可以接受的,超过 120 毫秒就有些难受了,最好还是在 70 毫秒以下。而 Hetzner 的服务器呢?刚刚 ping 了一下,肉眼平均在 350 毫秒左右。我平时部署的服务都是基于 HTTP 的,像是什么 wikijs 这种个人知识库,他们对延迟不敏感,我也没什么体感。但这个延迟肯定是玩不了 Minecraft。但是国内服务器这么贵,我真的用不起。

这里其实还可以考虑按时租用,比如说晚上要和朋友玩了,就把服务器打开,不用就关掉。阿里云和腾讯云都能够在关机时做到不收取实例配置费,你只需要支付硬盘的价格就行了。但是这也有一个弊端:你要是一不小心忘了,那就自求多福吧。

当然,你也可以考虑使用各家的产品来组合一套解决方案,例如通过一个 HTTP 请求或者一封邮件在触发服务器启动,然后在服务器内通过脚本检测游戏人数,如果半小时内玩家数量一直为 0 则触发自动关机。但这种解决方案通常不具有通用性,所以这里就不多介绍了,因为我确实没有这个需求。等之后有这个需求的,可以考虑研究研究。

其实我有公网 IP,只不过是 v6

由于 v4 地址日渐枯竭,我搬到昆明之后,发现这边的电信用的是 CGNAT,它是一种无感知的技术,旨在提供一个 Full-cone NAT 让多个用户共享一个 IPv4 地址。这也就是为什么基于 UDP 协议的 Tailscale 能够穿墙打洞成功,因为它是对称的 NAT。当然了,我也有 IPv6,实际上 Tailscale 穿墙打洞之后用的也是 IPv6 地址直连。但关于 IPv6 地址有很多系列,比如我现在一个 ip addr 下去:

  • 3: wlo1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
  • link/ether 【我的网卡MAC】 brd ff:ff:ff:ff:ff:ff
  • altname wlp0s20f3
  • inet 192.168.X.X/8 brd 192.168.X.255 scope global noprefixroute wlo1
  • valid_lft forever preferred_lft forever
  • inet6 【v6前缀】:2040:d278:cadf:b885/64 scope global temporary dynamic
  • valid_lft 85567sec preferred_lft 13567sec
  • inet6 【v6前缀】::1002/128 scope global dynamic noprefixroute
  • valid_lft 84998sec preferred_lft 84998sec
  • inet6 【v6前缀】:987:474a:409a:32d1/64 scope global temporary deprecated dynamic
  • valid_lft 85567sec preferred_lft 0sec
  • inet6 【v6前缀】:b432:6cc4:9984:3432/64 scope global dynamic mngtmpaddr noprefixroute
  • valid_lft 85567sec preferred_lft 13567sec
  • inet6 fe80::【v6本地链路】/64 scope link noprefixroute
  • valid_lft forever preferred_lft forever

除了 fe80 开头的本地链路地址之外,你可以看到我有 4 个不同的 v6 地址,他们都以运营商分配的前缀开头,他们的标签分别是:

  • global temporary dynamic
  • global dynamic noprefixroute
  • global temporary deprecated dynamic
  • global dynamic mngtmpaddr noprefixroute

首先他们都是 global,说明都是全局地址,也就是公网 IPv6。其次他们也都是 dynamic,说明都是动态分配的,因为我买不起固定 ip 的商宽。再后面有的有 temporary,有的没有。带有 temporary 的地址主要以出站为主,并在一段时间后会时效,这里是为了缓解访问的网站记录 IP 地址的问题。理论上你可以用这类地址监听入站,但取决于防火墙、路由表等诸多原因,你可能连不通,同时系统还会轮换这些 ip,实在不适合入站使用(废话,毕竟人家说了这个就是出站用的)。第三个 ip 还多了一个 deprecated,这个表示 ip 地址不会被新的连接所使用了,但由于目前还有持续的连接,这个 ip 还不能释放。

还剩下两个,比较复杂。一个是 mngtmpaddr,它对应了 IPv6 的隐私扩展(RFC3041),是操作系统随机生成出来的 ip 地址,顾名思义 —— 用来保护隐私的。另一个 noprefixroute,它意味着系统没有为这个 ip 创建前缀路由。所谓前缀路由,就是通过这个 IP 地址来访问一段 IP,比如 192.168.1.0/8 via 10.0.0.1,这个前缀路由意味着所有发往 192.168.1.0/8 这个网段的数据包都要发给 10.0.0.1。在家用情况下,我们不需要配置前缀路由,因为我们出站的数据包的第一站总是家里的路由器。不信你就看 ip -6 route show

  • 【v6前缀】::1002 dev wlo1 proto kernel metric 600 pref medium
  • 【v6前缀】::/64 dev wlo1 proto ra metric 600 pref medium
  • (docker和tailscale)
  • fe80::/64 dev wlo1 proto kernel metric 1024 pref medium
  • default via fe80::【路由器的v6本地链路】 dev wlo1 proto ra metric 600 pref medium

这个 defaul via ... 就是默认路由。

按理说 IPv6 普及了那么多年,家宽和蜂窝网络都应该支持了。在关闭了 TPLink 的 IPv6 防火墙之后,我用手机流量成功地使用那个 / 128 的地址访问到了电脑。手机是北京移动(在昆明),家宽是昆明电信。我的朋友在江苏,他也能在手机上用流量访问到我的电脑。

Minecraft 的协议用的是 tcp,所以只要一方能连上,这个链路就是通的。如果其他游戏使用 UDP,那么你还得确认运营商流量出口的 NAT 是不是 full cone。之前电脑上测试 tailscale 穿墙打洞失败,要么是手机热点的问题,要么是运营商 NAT 的问题,总之就是无法实现 UDP 直连。

这个方法是今天突然想到的,之后等他再去单位值班的时候试试。在发现这个方法之前,我们使用的是腾讯云的服务器做流量转发。

到底还是需要一个 v4 公网 IP。。。?

之前由于 Tailscale 穿墙打洞失败,我便认为流量没有 v6 地址,因为蜂窝网络是运营商自己的一个局域网。于是便很快地将思路转到了公网 v4 地址。在脑子里过了一遍诸如 nginx、frp 这些流量转发软件之后,我认为当下最快的解决办法就是用 ssh 做流量转发,因为我的 NUC 是 Linux 系统,所以自带 SSH。

因为当时我们都开了游戏,然后发现 tailscale 不能用。为了尽可能快地玩儿上游戏,虽然我之前在跨国 ssh 的时候遇到过断流的现象,但我猜测国内 ssh 流量应该不会有断流,除非有省墙,但目前云南省没有这些妖蛾子,所以应该没有问题。

于是果断地去腾讯云开了一个最便宜的配置,区域选的成都,因为在我们俩之间。这个服务器的唯一目的就是转发流量,所以硬盘拉到最小,网络选择按量付费,把带宽拉满 —— 跑图的时候轻量服务器给的不到 10Mbps 的带宽根本不够用 —— 流量用不了多少,主要是速度,你也不想朋友玩的时候加载个区块要等半分钟吧。总之就是配置最低,网络最高。这里记得给公网 ip,然后打开安全组。奔放一点的就全开,不想奔放就只开 mc 和 ssh 的端口号,分别是 25565 和 22。

SSH 本身有三种转发方式:

  • 动态转发(ssh -D localPort ...):通过 socks5 代理的方式,将发往本地端口的请求转发到服务器上,服务器上的出站端口随机选择(动态),可用于访问局域网资源。例如我通过动态转发在本机开设一个 socks5 代理,通过 tailscale 连上家里的服务器,那么我便可以通过这个 socks5 代理访问家里无法使用 tailscale 连接的资源,例如家里的路由器等。
  • 本地转发(ssh -L localPort:targetHost:targetPort ...):将发到本地端口的流量转发到远端指定服务器上。例如我的本地端口是 8080,而远端可以设定为 192.168.1.1:80(路由器的管理界面),这样当我连上家里的服务器时,只要我访问 localhost:8080 就可以直接打开路由器的管理界面。注意,远端的地址是对应服务器来说的,毕竟出入站还是在服务器上。
  • 远程转发(ssh -R remotePort:targetHost:targetPort):将服务器指定端口的入站转发到本地端口上。例如服务器绑定 25565 端口,本地绑定 127.0.0.1:43251(MC 分享局域网世界的端口),这样当别人访问服务器的 25565 端口时,数据将被自动转发到我本地的 MC 端口上,朋友们就可以通过这个服务器来连接到我家里的 MC 服务器了。

很明显,远程转发是我们需要的。在开始远程转发之前,请检查 /etc/ssh/sshd_config 里面的 GatewayPorts 是否为 yes,如果是 no(默认值),那么 remotePort 只能在 localhost 上面监听,而我们想要的是在 0.0.0.0:: 上监听。修改配置文件后记得重启或 reload 服务。

假设 Minecraft 打开局域网共享之后给出的端口号是 12345,而我们想直接转发到服务器上的 25565 端口上(也就是 MC 的默认端口号),则需要执行如下命令:

  • ssh -R 25565:localhost:12345 user@server -N

一般来说 user 都是 root,server 就是你的服务器 IP。如果你开 MC 的电脑不是 Linux,但你有另一台 Linux 并希望在上面转发,可以执行这个命令:

  • ssh -R 25565:192.168.1.2:12345 user@server -N

这里把 localhost 换成了 192.168.1.2,这个要换成你实际运行 mc 的电脑的 ip,并确保 linux 系统能够访问到这个端口(指检查防火墙之类的东西)。

这里额外加了个 -N,因为我们的目的是端口转发,因此这个 flag 可以告诉 ssh 不要打开一个 shell,不加也无所谓。

在成功转发之后,可以到服务器上使用 lsof -i:25565 看一下有没有正确监听端口,如果监听的端口是在 localhost 或者 127.0.0.1 上,那么请检查配置文件,并使用 ssh -R 0.0.0.0:25565:192.168.1.2:12345 user@server -N 来明确告诉 ssh 你要监听在所有端口上。

至此,朋友使用服务器的 ip 便可以成功连接到我 NUC 上分享的 Minecraft 了。

- 全文完 -


知识共享许可协议
【歪门邪道】想办法让朋友连上我家里的 Minecraft 服务器天空 Blond 采用 知识共享 署名 - 非商业性使用 - 相同方式共享 4.0 国际 许可协议进行许可。
本许可协议授权之外的使用权限可以从 https://skyblond.info/about.html 处获得。

Archives QR Code
QR Code for this page
Tipping QR Code
Leave a Comment

6 Comments
  1. 又学到了知识。

  2. 还好我是电信公网 IP 用户:)
    本地 PC + DDNS + 一个域名即可让好友愉快体验联机快乐

    1. @MoXiify 我这边只有公网 v6,我的下一篇文章就是介绍利用 cloudflare 做 ddns 的。不过 cf 只能接入二级域名,所以不得不又买了个域名给 cf 用(

    2. @天空 Blond 电信还是蛮爽的 (๑´・ω・) "(ㆆᴗㆆ)

    3. @MoXiify 毕竟世界加钱可及(

  3. 太厉害了٩(ˊᗜˋ*)و