FRP内网穿透实战:Dayz联机中继
前言
最近突然心血来潮打算和朋友玩玩Dayz这款老游戏。这游戏联机模式比较特殊,因为游戏地图大对象多、服务器端运算量十分大,因此并不能像别的大部分联机游戏一样房主创建房间后直连,而是必须自行搭建游戏服务器。
关于服务器搭建,由于我的云端公网服务器配置不够(常规云应用服务器,非专门的游戏服务器),所以服务器进程不能够跑在云端,那么也就只有我本地机来做运算了,服务器开在我的本地。但是这又引入一个问题——服务器跑在本地很简单,因为官方提供了完整的一键启动模板,但是这么一来我的服务器也只能被局域网段看到。也就是说朋友必须得和我处于同一网段才行,这自然是不现实的。于是核心问题就落在了:我本地产生的UDP游戏数据流怎么和远端的朋友主机产生交互。
P2P 打洞落败
为了能够快速玩上,一开始我们决定使用Radmin直接P2P打洞。提到打洞,这里必须涉及到NAT的几种形态:全锥形NAT、受限锥形NAT、端口受限锥形NAT、对称NAT。本篇文章不详细讲述这些NAT形态。
通信双方均为锥形:
这是P2P打洞最简单的一种,A、B机双方在STUN中转服务器拿到对方各自开在公网网关的端口后,各自互相针对指定端口发个包就能将NAT捅开,成功打洞。一方锥形、一方对称形/双方对称形:
这个方式打洞的难度就超级大了,因为对称形NAT只要发现目标的IP/端口不一致,就会采用随机端口映射分发一个随机端口给这个新连接的工作流。而对端拿到的端口是从STUN服务器来的旧端口,这就好比你拿着A门的钥匙想去开B门,B门自然不会认。见下图。
很不幸的是,我们之间有一方正是对称NAT,Radmin打洞无效只能走TURN中继服务器。而Radmin的中继服务器在海外,意味着我们的游戏数据从我本地发出后需要出国兜一圈再回到我朋友手里,那个延迟自然很大,很多时候根本无法正常游戏。
FRP 云端流量中继服务
没办法,便利的方式用不了,自然只能利用手里这个低配云服务器搭一个国内中转了。这里要先简单介绍一下FRP是什么:FRP全称(Fast Reverse Proxy)。快速反向代理工具,最核心的用途就是拿来内网穿透。什么是内网穿透?说白了就是利用一台具有公网IP的设备把你内网设备(不具备公网IP)的某个服务端口暴露到公网环境中。本案例中我的本地主机和朋友的主机都在内网中,打洞失败的情况下,双方不管谁连谁都不可能连上。
FRP原生支持TCP/UDP的代理,这正是我们需要的。只要成功代理,我们本地的流量就可以直接在国内中转,延迟将会大大下降,提高游戏体验。当然这篇文章我只是梳理整个架构,不讲部署。
整个通信流程如下:
- 本地:游戏服务器跑在本机A,监听在本地
127.0.0.1:2302环路。并负责产生原始游戏数据(也就是流通在整个信道上的数据流)。同时本机A也运行一个frp客户端frpc,配置了将流量全部转发到本地的2302端口。frpc启动时会主动去连接云端frps服务,并在token认证成功后,向云端声明:我目前有个udp代理需求,请监听公网2302端口,并将进入该端口的流量全部通过隧道转发给我。 - 云端:云端frps收到代理声明后,开始监听UDP/2302端口。
- 数据出站(本地服务器->中转站->对端主机):本地服务器运算完毕并产生数据后,若当前无任何连接,则静默不进行任何动作。若当前有连接,则将数据封装成UDP报文后发回给本地frpc,再由frpc将服务器UDP数据使用TCP(FRP默认使用TCP作为底层隧道协议)进行封装后通过隧道发回给云端。云端frps收到后解包还原出UDP数据,再发给目标主机(朋友们)。
- 数据入站(朋友们->中转站->本地服务器):当朋友的游戏客户端向云端
中转站公网IP:2302发送UDP游戏报文后,frps由于在监听云端2302端口,所以会对收到的UDP游戏报文使用TCP进行封装。而后通过反向长连接通道将数据送回给本机A的frpc,frpc将隧道里过来的TCP报文解包还原出UDP游戏数据报文后,路由给本机127.0.0.1:2302,也就是Dayz服务器监听的地址。服务器就这样收到了远端的游戏数据。
其它
这里需要注意两点,首先是Dayz服务器的查询端口。如果你只代理了2302/UDP游戏端口,那么玩家可以通过IP直连进入游戏,但是在游戏登录客户端里面是找不到你服务器的信息的。为了解决这个问题,我们还需要代理27016/UDP(游戏默认服务器查询端口)端口,同时正确设置防火墙和出入站规则。
然后就是关于隧道的传输协议。默认是TCP协议搭建的隧道,除此之外官方也提供了QUIC、KCP等方式来进行传输控制,各有优劣,这里不展开。
Ps:这里聊个题外话——前面我们提到如果什么都不做,那么服务器只能在局域网段运行。如此的话本地网段的主机又是怎么发现我们的呢?
这其实很简单:点击客户端的LAN按钮后,客户端会发送一个基于UDP的Steam A2S信息查询包。该包被广播到网段中其它设备,其它设备底层网络栈收包后,如果没有应用层监听该端口,内核会静默丢弃该包;即便有其他应用误绑了查询端口(例如DayZ默认27016/UDP),由于无法解析A2S协议头,也不会产生响应;只有正确实现了A2S协议的 DayZ服务端,才会解析请求并单播回复服务器元数据。然后我们收到服务器的回复后,在页面上就会显示当前网段有哪个服务器正在运行,以及服务器的一些基础元信息了。
END
FRP内网穿透实战:Dayz联机中继