本文主要是介绍基于Java实现的元气骑士公网异地联机游戏设计,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
资源下载地址:https://download.csdn.net/download/sheziqiong/87926401
资源下载地址:https://download.csdn.net/download/sheziqiong/87926401
元气骑士公网异地联机
初探局域网联机过程
前言
元气骑士作为当下热门的单机游戏,很多人都在琢么怎么和好朋友即使不在一个局域网内也能一起玩耍。目前网上比较成熟的方案是在服务器上搭建 VPN,两个手机通过连接 VPN 来穿透内网,实现彼此的连通,再通过联机工具转发房间的广播信息,如此就能做到公网异地联机了,相关的教程 CSDN 上都有。
本学期正式学习了计算机网络、windows API 编程等课程,正想对这些课程进行一个实践总结,我打算不通过 VPN,而是通过自己编程实现元气骑士的公网异地联机。
不过要实现公网异地联机,需要做到转发数据报、穿透内网,本篇主要就是通过封包分析,搞明白这个游戏局域网联机过程,从而弄明白,要转发数据报应该监听哪个端口。
在这个过程中我用了两种方法监听游戏的封包,第一种是电脑开热点,Wireshark 监听虚拟 wifi 所对应的的网卡,这种方法能收到广播信息,但是收不到点对点的 UDP 数据报,随后根据所学内容对问题进行分析,找到了问题的原因。第二种方法是用 tcpdump 直接监听安卓设备,导出封包信息,用 Wireshark 进行分析。
对于当前我们研究的问题来讲,第二种方法才是恰当的方法,使用第二种方法既能对创建房间时的广播进行监听,又能对游戏进行时的 P2P 进行监听,但是我还是打算把第一种方法写上,一方面是对自己分析过程的记录,另一方面也希望能提醒自己遇到问题不要想当然,要有扎实的理论基础,从理论出发去分析问题。
读者若是不感兴趣可以直接跳过 监听创建房间阶段的封包数据 与 监听游戏进行阶段的封包数据(错误方法) ,可直接从 监听游戏进行阶段的封包数据(正确方法) 开始阅读。
分析工具
Wireshark
tcpdump
逍遥安卓模拟器工作室版
获取封包
监听创建房间阶段的封包数据
搭建测试环境
第一步:配置电脑搭建一个热点,ssid 为 jack,密码为 jackjackjack
@echo off
netsh wlan set hostednetwork mode=allow ssid=jack key=jackjackjack
net start "Windows Firewall"
netsh wlan start hostednetwork
pause
12345
第二步:测试将测试所用的两部安卓手机连接到我们电脑搭建的热点上
第三步:用 Wireshark 监听虚拟 wif 所对应的网卡
监听并分析创建房间时的封包数据
使用手机连接电脑搭建的热点,随后进入元气骑士,创建一个房间,观察到有 UDP 封包如下:
192.168.137.110 是手机接入热点后,DHCP 给手机分配的内网 ip,当有玩家创建游戏房间时,玩家所在主机(192.168.137.110)会往 192.168.137.255 发送一封 UDP 封包,可见封包内容包括版本号、房间名和一些其他的配置信息。192.168.137.255 是一个特殊的地址,它最后 8 为全是 1,说明这是一个广播地址,向这个地址发送的数据会广播给同一网段下(192.168.137.0/24)下的其他主机。
监听游戏进行阶段的封包数据(错误方法)
将另一台主机连接热点,加入游戏,观察 Wireshark 监听的封包情况:
当另一台手机加入游戏后,Wireshark 并没有监听到什么可疑的封包,只是主机创建房间的消息一直在广播,之后我开始了游戏,房间内只有我的两部手机,手机 A 的角色移动也同步传输到了手机 B,但是 Wireshark 中并没有从 A 到 B 的数据包(无论 TCP 还是 UDP 都没有),这曾经让我一度陷入困惑。
随后我又观察到,在游戏的过程中,不断能接收到 ARP 封包,在广播寻问手机 A(192.168.137.110)、手机 B(192.168.137.92)的物理地址。
这说明,他们之中绝对有通信,只是 Wireshark 并未监听到。
问题分析过程:
出现了理论上应该存在、但是却监听不到的封包着实让我头疼。期初我的想法是,两个游戏角色的行为可以同步,说明他们之间绝对有数据往来,二者都连接到我电脑的热点上,按理说,我电脑虚拟 wifi 的网卡是二者数据往来的必经之路,只要在这里设下监听,什么数据都应该尽收眼底,所以我曾一度认为是软件配置和使用方法的问题,但我又能找到方法证明软件和配置都没有问题,那么问题出在哪里呢?我从中午思考这个问题一直到凌晨两点都未曾想通,要是这个问题解决不了,后面也无法继续了。
凌晨两点,我关了房间的灯,准备卧床睡觉,在床上我尽力回忆着计算机网络相关的理论知识,企图推出问题的原因,我想到了我的计网老师,我努力回想她上课讲过的每一句话……甘霖娘,我没去上几节课,上课也复习数学来着,仅剩的记忆也就是她最后一节课让我打扫实验室来着……
这事不怪老师,是我自己没去上课,虽然我没去上过课,但是书我还是看了,书中自有黄金屋,我又开始努力想,书上都写了什么?突然一句话映入我的脑海
我们知道适配器有过滤功能。但适配器从网络上每收到一个 MAC 帧就先用硬件检查 MAC 中的目的地址。如果是发往本站的帧则收下,然后再进行其他处理。否则就将此帧丢弃,不再进行其他处理。
——《计算机网络》第七版 P95 页 第四段
我清楚的记得这句话,上述引用是书中原文,这句话让我印象非常深刻,因为它有错别字!
但适配器从网络上每收到一个 MAC 帧就先用硬件检查 MAC 中的目的地址。
这句话的第一个字显然是错字,此处用”但”逻辑不通,应该“当”,作者打字的时候少打了个“g“。
目前的路由器兼有交换机的功能,加之监听到的数据包中有很多 ARP 协议,事件的全貌一下就理出来了:
- 当手机 B 玩家加入游戏后,B 得到了手机 A 的 IP 地址
- B 向 A 发送 IP 报,IP 报在数据链路层封装成帧,通过 ARP 协议得到了 IP 对应的 MAC 地址
- 当 B 发送出去的 MAC 帧传输到我的电脑时,我电脑负责交换机功能的那一部分硬件,直接将 B 发送的帧转发给了 A,并没有向上传输
- Wireshark 监听我电脑负责路由的那一部分,B 发送给 A 的 MAC 帧没到这一层,所以监听不到 AB 之间直接通信的数据报
混杂模式
按理说,开启混杂模式,应该就可以监听到 MAC 帧了,但是这里我并未成功,具体原因不明,先 mark 日后探究。
监听游戏进行阶段的封包数据(正确方法)
总之,进行到这一步,想获取游戏进行阶段的封包数据,只在网关监听是万万不行的了,必须想办法在安卓上直接对设备网卡进行监听。幸亏 Android 和 Linux 有着千丝万缕的联系,在安卓上可以直接执行 shell,这才为在安卓上拦截封包提供了可行性。想实现封包的拦截需要有:
- 一台支持 adb 的、已经 root 的手机
- tcpdump 工具
我使用的是逍遥安卓模拟器工作室版,在它根目录下有一个叫 memuc.exe 的工具,可以提供 ADB 支持,同时该模拟器默认就是已经 ROOT 的,很好满足了我的第一个需求。
按住 Shift,右键点击根目录文件件空白处,选择在此处打开命令行窗口,键入一下指令
memuc.exe adb -i 0 shell
可对编号为 0 的虚拟机执行 shell 命令
在安卓机装入 tcpdump 并开启监听的方法可以参考:
https://www.cnblogs.com/likwo/archive/2012/09/06/2673944.html
搭建实验环境
为实现模拟安卓在局域网联机的环境,又需要如下两点:
- 模拟器多开,至少 2 台
- 模拟器连于同一路由下
第一点逍遥模拟器的多开管理器已经做到了,第二点需要稍微配置一下。
找到设置-》网络设置,将网络模式设置为桥接,点击详细设置,IP 设置为 DHCP。
两台虚拟机都这样设置,注意:逍遥模拟器只有工作室版有网络模式设置这个功能,个人版没有,所以特别注明是工作室版。
执行监听
进入 shell 模式
memuc.exe adb -i 0 shell
在 shell 模式下,输入:
tcpdump -n -v -w "/sdcard/capture.pcap" src host 192.168.137.82 or src host 192.168.137.217
这里的在 192.168.137.82 和 192.168.137.217 是我两台模拟器的 ip 地址,可以在设置-》关于手机-》状态信息中查看到。
模拟器上完成创建游戏、加入游戏、行走、使用技能、退出游戏等操作。
在命令行中按 ctrl+c 退出,输入
memuc.exe adb -i 0 pull /sdcard/capture.pcap capture.pcap
将 capture.pcap 文件从模拟器中下载出来,随后用 Wireshark 加载该文件,进行分析。
封包分析
将刚才得到的封包数据,按时间进行排序,去掉无用的封包,从创建房间封包开始分析。
如此我们可以推断出,创建房间的主机 IP 为 192.168.137.217
随后收到的是来自 192.168.137.82:35198
,发送给 192.168.137.217:7777
的封包
同时我们也看到, 192.168.137.217:7777
接收到封包后立刻给 192.168.137.82:35198
回了一个。
追踪这个过程的 UDP 流,我合计,这应该就是我想要的,是游戏交互过程用于同步的数据报。
有些数据报包含了武器、生命、能量等信息,应该是用于开局同步玩家信息用的,不过这数据报几乎就是明文……要是……岂不是……。
结语
如此,整个游戏局域网联机的具体实现流程就比较清晰了,元气骑士之间的通信用的全部都是 UDP,其通信流程大体如下:
- 主机 A 创建游戏房间,将房间信息在局域网内 23333 端口广播,同时监听自己的 7777 端口
- 主机 B 接受到了 A 的广播信息,当 B 想加入 A 的时候,B 就启用一个随机端口,向 A 地址的 7777 端口发送数据
- A 的 7777 端口监听器,发现了 B 的数据,记录 B 的发送端口,并立刻向 B 的发送端口发送响应数据
- B 给 A 发数据就发 7777 端口,A 给 B 发送数据就往 B 第一个数据报的发送端口发,如此实现两个游戏的通信
房间信息在局域网内 23333 端口广播,同时监听自己的 7777 端口
2.主机 B 接受到了 A 的广播信息,当 B 想加入 A 的时候,B 就启用一个随机端口,向 A 地址的 7777 端口发送数据
3.A 的 7777 端口监听器,发现了 B 的数据,记录 B 的发送端口,并立刻向 B 的发送端口发送响应数据
4.B 给 A 发数据就发 7777 端口,A 给 B 发送数据就往 B 第一个数据报的发送端口发,如此实现两个游戏的通信
要实现异地局域网联机,其实可以不用关心 A 给 B 发了什么,也不用关心 B 给 A 发了什么,只需要实现上述端口的监听与转发即可,现在已经迈出了第一步了,终于搞清楚转发哪些端口了,下一步就是得实现一个支持内网穿透的全双工转发机了,这涉及网络编程、多线程等知识。
两种方案可行性分析
前言
在上一篇中,我已经通过分析得知了元气骑士在局域网联机过程中是通过 UDP 进行交互的,只要我们能正确的转发这些 UDP 消息,就能实现异地公网的联机了。转发 UDP 消息通常有两种方案:一个是通过 UDP 穿透,建立起两部手机的对等连接,让两部手机直接向对方发送 UDP;另一个是搭建一个代理服务器,手机先将 UDP 消息发送给服务器,服务器将消息再转发给另外一部手机。通过本篇的分析可以得知, 对于元气骑士公网异地联机而言,方案一是不可行的 ,最后我只能选择方案二进行尝试。方案一虽然不可行,但是通过对方案一的可行性分析,学习了 NAT 与 UDP 穿透的相关知识,也算收获匪浅,故写下本篇博文记录一下。如果对 NAT 以及 UDP 的穿透不感兴趣可跳过本篇。
方案一:UDP 穿透,实现异地联机
为什么实现联机需要进行 UDP 穿透
由于互联网用户的急速增长,每个用户又拥有众多上网设备(笔记电脑、智能手机、iPad 等),从 20 世纪 80 年代起,一个很明显的问题是 IPv4 地址在以比设计时的预计更快的速度耗尽。
为了缓解这一问题,计算机网络领域提出了网络地址转换(Network Address Translation,缩写为 NAT)技术。目前大多数手机、电脑所分配到的 IP 其实都经过了多次 NAT 转换。
例如:
10.125.225.198 是我在设置-》关于手机-》状态信息中所得到的 ip 地址,是移动运营商分配给我的 ip 地址,而我去浏览器查询 ip 时却显示的是 223.104.175.159 ,这说明在运营商那,我的 ip 地址经过了至少一次的转换。
显然,10.125.225.198 是内网 ip,223.104.175.159 才是能上网的公网 ip,此刻我的手机并未连接路由器,使用的是移动流量。如果手机连接了路由器,则内网 ip 为 192.168.. 公网 ip 为路由器拨号所得的 ip,在网络请求时,路由器会将将我们的地址进行了一次转换,这种地址转换技术就是 NAT。
当内网设备向公网发起请求的时候,不同的内网 ip 会被路由器转化为同一个外网 ip(因为路由器拨号上网只得到了一个外网 ip),那么问题来了,如果外部网络想向内网设备请求会怎样呢?如果不考虑其他技术或协议的话,外部网络设备只知道一个外网 ip, 当外部网络设备向这个外网 ip 发送数据的时候,路由器会懵逼,因为它并不知道该把这个源自外部消息转发给内网哪个设备。
现实情况是,我们的路由器不会那么傻,大多数情况下(几乎百分百)我们能正确收到外部网络返回的响应信息(外部网络的响应信息,是由外部服务器向本地主机发送的信息,“响应”说明服务器给本地主机发送信息之前,本地主机必须先发起请求)。
NAT 有三种类型:静态 NAT(StaticNAT)、动态地址 NAT(PooledNAT)、网络地址端口转换 NAPT(Port-LevelNAT)
我们最常见的是网络地址端口转换 NAPT,NAPT 在处理内网设备向外网的请求时,会将地址和端口一并进行转换,假如 A 主机的发送端口是 80,可能转换后发送端口就变成了 6000,若 B 主机也发送了一个请求,那么 B 主机的发送端口可能就是 60001 了,总之在一个时间段内,对于不同主机对外网的请求来说,NAPT 一定会给他们分配不同的发送端口, 这样服务器返回响应信息的时候,就可以根据信息的目的端口,来准确的识别将信息转发给哪个一个设备了 。而穿透就是指,让路由器知道当两个不同内网的设备进行通信时,该如何正确的转发消息。
UDP 穿透流程
当下,我们面临情况是手机与手机点对点的通信,无论用手机流量还是用 wifi,总之,是内网设备对内网设备的 P2P 通信,这种情况的 UDP 穿透需要一个 具有外网 IP 的 服务器,其穿透流程大体如下:
- 主机 A 通过路由器向服务器发送 UDP 数据报
- 路由器 A 转换地址与端口
- 服务器记录下路由器 A 的地址和端口
- 主机 B 通过路由器向服务器发送 UDP 数据报
- 路由器 B 转换地址与端口
- 服务器记录下路由器 B 的地址和端口
- 服务器向 B 发送 A 的端口和地址
- 路由器 B 会把服务器的信息转发给主机 B
- 主机 B 向服务器返回的 A 的端口和地址发送请求
- 路由器 B 将消息转发给路由器 A(消息会被路由 A 抛弃)
- 服务器向 A 发送 B 的端口和地址
- 路由器 A 会把服务器的信息转发给主机 A
- 主机 A 向服务器返回的 B 的端口和地址发送请求
- 路由器 A 将消息转发给路由器 B(消息会被路由 B 转发)
- 主机 B 会接受到经过路由 B 转发的、主机 A 的消息
- 此后主机 A 和主机 B 就可以无需经过服务器了,可以直接通信了
要实现上述穿透流程,有一个必要条件,就是路由器 B 向服务器发送请求时转换的地址和端口,必须和路由器 B 向路由器 A 发送请求时的地址和端口一致,也就是说,对于同一内网 ip,在段时刻内即使请求目标是不同的外网 IP,转后的端口也相同。 但事实上,并不是所有的 NAPT 都能满足这个条件。
NAT 类型
NAPT 总共有 4 中类型,分别是:
NAPT 类型 | 别称 | 端口转换策略 | 将外网设备消息转发给内网设备 A 的条件 |
---|---|---|---|
Full Cone NAT | NAT1 | 所有来自同一 个内网设备的请求均被转换为同一端口 | A 曾经用该端口向外部任意地址发送过消息 |
Restricted Cone NAT | NAT2 | 所有来自同一 个内网设备的请求均被转换为同一端口 | A 曾经用该端口向消息的发送地址发送过消息 |
Port Restricted Cone NAT | NAT3 | 所有来自同一 个内网设备的请求均被转换为同一端口 | A 曾经用该端口向消息的发送地址与发送端口发送过消息 |
Symmetric NAT | NAT4 | 只有来自于同一个内网设备、针对同一目标的请求才被转换为同一端口。来自同一 个内网设备、针对不同目标的请求会被转换为不同端口 | A 曾经用该端口向消息的发送地址与发送端口发送过消息 |
根据端口转换策略,可将 NAPT 分为锥型(Cone)和对称型(Symmetric),锥型包括 NAT1、NAT2、NAT3,锥型 NAPT 会将所有来自同一 个内网设备的请求均转换为同一端口 ,而对称型 NAP 也称 NAT4 是只有来自于同一个内网设备、针对同一目标的请求才被转换为同一端口。
如果想实现 UDP 穿透,至少两端之中有一端是锥型 NAT 才可以,所以在确定方案可行性之前,需要测定手机流量的 NAPT 类型。
NAPT 类型测试
测试 NAPT 类型有专门的协议:STUN ,简单的办法就是,去网上下载一个 NAT 类型测试工具,这个 CSDN 就有资源。因为考虑到元气骑士联机,所以用电脑连接手机热点,然后在电脑上用 NAT 测试工具试一试就能知道个大概,
但非常不幸,手机热点是对称型的。
通过查阅资料,基本确定 3G/4G 流量都是对称型的,所以这个方案凉凉。
结论
因为手机流量、手机热点都是对称型 NAT,所以无法实现 UDP 穿透,故该方案不可行。
方案二:建立代理服务器,转发 UDP 消息
幸好通过之前的分析,已经得知元气骑士在局域网联机的时候,只会发送 23333 端口的广播信息,和 7777 端口的单一目标信息,但是要是公网联机,需要三个转发机,分别是:
- 主机 A 和主机 B:用于将手机的消息转发给服务器,将服务器的消息转发给手机
- 服务器:用于将主机 A 的消息转发给主机 B,将主机 B 的消息转发给主机 A
所以代理服务器的工作流程大致如下:
步骤 | 操作 |
---|---|
1 | 主机 A 与服务建立连接 |
2 | 主机 B 与服务器建立连接 |
3 | 手机 A 创建游戏房间,发送到 23333 端口的广播消息 |
4 | 主机 A 监听 23333 端口,发送端口不变,将接受的消息发往服务器的 34444 端口,并记录手机 A 的内网地址 |
5 | 服务器监听 34444 端口,发送端口不变,将接受的消息发往主机 B 的 34444 端口 |
6 | 主机 B 监听 34444 端口,发送端口不变,将接受的消息转发为发送到 23333 端口的广播 |
7 | 手机 B 监听 23333 端口,发送端口不变,接受到 23333 端口的消息后,向主机 B 的 7777 端口发送加入房间的消息 |
8 | 主机 B 监听 7777 端口,同时开始监听消息的发送端口,并将接受的消息发送端口不变的发往服务器的 8888 端口,并记录手机 B 的内网地址 |
9 | 服务器监听 8888 端口,同时开始监听消息的发送端口,并将接受的消息发送端口不变的发往主机 A 的 8888 端口 |
10 | 主机 A 监听 8888 端口,同时开始监听消息的发送端口,并将接受的消息发送端口不变的发往手机 A 的 7777 端口 |
11 | 手机 A 监听 7777 端口,手机 A 收到消息立即向消息来源返回消息 |
12 | 主机 A 监听之前的发送端口,将接受的消息不改变目的端口和发送端口的转发给服务器 |
13 | 服务器监听之前的发送端口,将接受的消息不改变目的端口和发送端口的转发给主机 B |
14 | 主机 B 监听之前的发送端口,将接受的消息不改变目的端口和发送端口的转发给手机 B |
整理一下,给出每个代理服务器的任务:
转发机 | 监听端口 |
---|---|
主机 A | 23333、8888、8888 端口消息的发送端口 p |
主机 B | 34444、7777、7777 端口消息的发送端口 p |
服务器 | 34444、8888、8888 端口消息的发送端口 p |
转发机 | 转发逻辑 |
---|---|
主机 A | 23333-> 服务器:34444;8888-> 手机 A:7777;p-> 服务器:p; |
主机 B | 34444-> 内网广播:23333;7777-> 服务器:8888;p-> 手机 B:p; |
服务器 | 34444-> 主机 B:34444;8888-> 主机 A:8888;p-> 主机 B:p; |
结语
方案一虽然不可行,但是通过对方案一的可行性分析,学习了 NAT 与 UDP 穿透的相关知识,也算收获匪浅。方案二虽然可行,但是因为要持续占有服务器资源,所以如果将此方案列为第二方案。方案二是假设 A 和 B 两个玩家的联机情况,我的想法是,先完成双人联机的情况,再此基础上再逐步扩展。转发机我打算用 Java 实现,因为 Java 网络与多线程的编程封装了很多实用的 API,且 Java 具有良好的移植性
完善转发机的转发规则
前言
在上一篇中,我通过表格对转发机的监听端口和转发逻辑进行了简短的描述。
转发机 | 监听端口 |
---|---|
主机 A | 23333、8888、8888 端口消息的发送端口 p |
主机 B | 34444、7777、7777 端口消息的发送端口 p |
服务器 | 34444、8888、8888 端口消息的发送端口 p |
转发机 | 转发逻辑 |
---|---|
主机 A | 23333-> 服务器:34444;8888-> 手机 A:7777;p-> 服务器:p; |
主机 B | 34444-> 内网广播:23333;7777-> 服务器:8888;p-> 手机 B:p; |
服务器 | 34444-> 主机 B:34444;8888-> 主机 A:8888;p-> 主机 B:p; |
但是因为路由器和 3G 或者 4G 网络存在对称型 NAPT 的问题,所以实际的转发逻辑并非像表格总那么简单。同时,服务器要实现转发,需要知道接受谁的信息转发给谁,也就是说主机 A 和 B 必须主动与服务器建立连接,服务器存储 A 和 B 相关的信息才能正确的执行转发。本篇将在考虑上述问题的基础上进行对问题进行讨论。
考虑对称型 NAPT、进一步确定转发规则
将主机 A、主机 B、服务器的转发规则按时间顺序进行排列得到如下表格:
时序 | 源地址 | 监听端口 | 源端口 | 目的地址 | 目的端口 |
---|---|---|---|---|---|
1 | 主机 A | 23333 | 服务器 | 34444 | |
2 | 服务器 | 34444 | 主机 B | 34444 | |
3 | 主机 B | 34444 | 局域网广播 | 23333 | |
4 | 主机 B | 7777 | 服务器 | 8888 | |
5 | 服务器 | 8888 | 主机 A | 8888 | |
6 | 主机 A | 8888 | 手机 A | 7777 | |
7 | 主机 A | p | 服务器 | p | |
8 | 服务器 | p | 主机 B | p | |
9 | 主机 B | p | 手机 B | p |
为着重关注主机 A、B 与服务器的交互问题,将上述规则按交互对象进行分组,每组规则按时序升序排列。即 1、5、6、7 作为主机 A 与服务器交互的一组,2、4、8、9 作为主机 B 与服务器交互的一组,完成后表格如下:
时序 | 源地址 | 监听端口 | 源端口 | 目的地址 | 目的端口 |
---|---|---|---|---|---|
1 | 主机 A | 23333 | 服务器 | 34444 | |
5 | 服务器 | 8888 | 主机 A | 8888 | |
6 | 主机 A | 8888 | 手机 A | 7777 | |
7 | 主机 A | p | 服务器 | p |
时序 | 源地址 | 监听端口 | 源端口 | 目的地址 | 目的端口 |
---|---|---|---|---|---|
2 | 服务器 | 34444 | 主机 B | 34444 | |
3 | 主机 B | 34444 | 局域网广播 | 23333 | |
4 | 主机 B | 7777 | 服务器 | 8888 | |
8 | 服务器 | p | 主机 B | p | |
9 | 主机 B | p | 手机 B | p |
先考虑未知端口数 p 的取值,通过第一篇的分析,已知元气骑士局域网联机具有这样一个规律:
Address:Port 向 7777 发送请求,元气骑士会用 7777 端口向 Address:Port 回一个响应
所以可以通过设置规则 6 的源端口,来控制 p 的数值,这里设置规则 6 的源端口为 9898,那么规则 7 的监听端口也应该是 9898,向服务器转发的目的端口、源端口,不妨设为 9999。
随后可以推断,规则 8 的监听端口与目的端口、规则 9 的监听端口,也应该设为 9999。
在第一篇的分析中还提及元气骑士局域网联机的另一个规律:
Address:Port 向 7777 发送请求,Address 的元气骑士就会监听 Port 端口,期望得到来自 7777 端口的响应.
所以规则 9 的源端口应设为 7777
最终得到下面两个表格:
时序 | 源地址 | 监听端口 | 源端口 | 目的地址 | 目的端口 |
---|---|---|---|---|---|
1 | 主机 A | 23333 | 服务器 | 34444 | |
5 | 服务器 | 8888 | 主机 A | 8888 | |
6 | 主机 A | 8888 | 9898 | 手机 A | 7777 |
7 | 主机 A | 9898 | 服务器 | 9999 |
时序 | 源地址 | 监听端口 | 源端口 | 目的地址 | 目的端口 |
---|---|---|---|---|---|
2 | 服务器 | 34444 | 主机 B | 34444 | |
3 | 主机 B | 34444 | 局域网广播 | 23333 | |
4 | 主机 B | 7777 | 服务器 | 8888 | |
8 | 服务器 | 9999 | 主机 B | 9999 | |
9 | 主机 B | 9999 | 7777 | 手机 B | p |
随后开始针对 NAPT 考虑源端口的设定,通过在第二篇中对 NAPT 的学习,我们知道下面一个结论:
Address1:Port1 向 Address2:Port2 发送过请求后,Address1 一定能收到 Address2:Port2 发往 Address1:Port1 的响应。
对于规则 1 与规则 5、规则 4 与规则 8 这两组规则,我们都可以看成是由主机向服务器发送了请求,服务器给予了响应,所以只需要 将请求报文的源端口设置为响应报文的目的端口,将响应报文的源端口设置为请求报文的目的端口 即可,即令 1 的源端口为 8888,5 的源端口为 34444;4 的源端口为 9999,8 的源端口为 8888。
规则 7、规则 2、规则 3 的源端口可以随便选取,这里就取与它们各自监听端口一样的值即可。
最终得到表格如下:
时序 | 源地址 | 监听端口 | 源端口 | 目的地址 | 目的端口 |
---|---|---|---|---|---|
1 | 主机 A | 23333 | 8888 | 服务器 | 34444 |
5 | 服务器 | 8888 | 34444 | 主机 A | 8888 |
6 | 主机 A | 8888 | 9898 | 手机 A | 7777 |
7 | 主机 A | 9898 | 9999 | 服务器 | 9999 |
时序 | 源地址 | 监听端口 | 源端口 | 目的地址 | 目的端口 |
---|---|---|---|---|---|
2 | 服务器 | 34444 | 34444 | 主机 B | 34444 |
3 | 主机 B | 34444 | 34444 | 局域网广播 | 23333 |
4 | 主机 B | 7777 | 9999 | 服务器 | 8888 |
8 | 服务器 | 9999 | 8888 | 主机 B | 9999 |
9 | 主机 B | 9999 | 7777 | 手机 B | p |
结论
为方便 Java 面向对象的编程设计,将上述规则,按源地址进行分类:
主机 A
监听端口 | 源端口 | 目的地址 | 目的端口 |
---|---|---|---|
23333 | 8888 | 服务器 | 34444 |
8888 | 9898 | 手机 A | 7777 |
9898 | 9999 | 服务器 | 9999 |
注:第二行源端口、第三行监听端口应该一致,这里设置为 9898,其实可以设为成任何值,一致就行,不建议设置已经出现过的监听端口,不方便进行本地测试。
主机 B
监听端口 | 源端口 | 目的地址 | 目的端口 |
---|---|---|---|
34444 | 34444 | 广播 | 23333 |
7777 | 9999 | 服务器 | 8888 |
9999 | 7777 | 手机 B | p |
注:p 等于 7777 端口所收到的报文的源端口,应在接受到 7777 端口的消息时动态设定。
服务器
监听端口 | 源端口 | 目的地址 | 目的端口 |
---|---|---|---|
34444 | 34444 | 主机 B | “ 34444” |
8888 | 34444 | 主机 A | “8888” |
9999 | 8888 | 主机 B | “9999” |
注:服务器需要向主机 B 发送目的端口为 34444 的请求,所以建立连接时,主机 B 需要向服务器发送以 34444 为源端口,34444 为目的端口的请求报文。服务器的目的端口都加上了引号,因为 NAT 的原因,服务器需要把数据发到 nat 转换后的端口。
结语
明确了哪个转发机该用什么端口转发向哪里之后,剩下的工作就简单多了,对于转发机的实现,仁者见仁智者见智,原理上不难理解,主要就是需要注意主机 A 与主机 B 需要先向服务器发送请求才能接收到服务器发过来的数据。
资源下载地址:https://download.csdn.net/download/sheziqiong/87926401
资源下载地址:https://download.csdn.net/download/sheziqiong/87926401
这篇关于基于Java实现的元气骑士公网异地联机游戏设计的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!