基于Java实现的元气骑士公网异地联机游戏设计

2023-10-09 02:10

本文主要是介绍基于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,这才为在安卓上拦截封包提供了可行性。想实现封包的拦截需要有:

  1. 一台支持 adb 的、已经 root 的手机
  2. 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

搭建实验环境

为实现模拟安卓在局域网联机的环境,又需要如下两点:

  1. 模拟器多开,至少 2 台
  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,其通信流程大体如下:

  1. 主机 A 创建游戏房间,将房间信息在局域网内 23333 端口广播,同时监听自己的 7777 端口
  2. 主机 B 接受到了 A 的广播信息,当 B 想加入 A 的时候,B 就启用一个随机端口,向 A 地址的 7777 端口发送数据
  3. A 的 7777 端口监听器,发现了 B 的数据,记录 B 的发送端口,并立刻向 B 的发送端口发送响应数据
  4. 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 的 服务器,其穿透流程大体如下:

  1. 主机 A 通过路由器向服务器发送 UDP 数据报
  2. 路由器 A 转换地址与端口
  3. 服务器记录下路由器 A 的地址和端口
  4. 主机 B 通过路由器向服务器发送 UDP 数据报
  5. 路由器 B 转换地址与端口
  6. 服务器记录下路由器 B 的地址和端口
  7. 服务器向 B 发送 A 的端口和地址
  8. 路由器 B 会把服务器的信息转发给主机 B
  9. 主机 B 向服务器返回的 A 的端口和地址发送请求
  10. 路由器 B 将消息转发给路由器 A(消息会被路由 A 抛弃)
  11. 服务器向 A 发送 B 的端口和地址
  12. 路由器 A 会把服务器的信息转发给主机 A
  13. 主机 A 向服务器返回的 B 的端口和地址发送请求
  14. 路由器 A 将消息转发给路由器 B(消息会被路由 B 转发)
  15. 主机 B 会接受到经过路由 B 转发的、主机 A 的消息
  16. 此后主机 A 和主机 B 就可以无需经过服务器了,可以直接通信了

要实现上述穿透流程,有一个必要条件,就是路由器 B 向服务器发送请求时转换的地址和端口,必须和路由器 B 向路由器 A 发送请求时的地址和端口一致,也就是说,对于同一内网 ip,在段时刻内即使请求目标是不同的外网 IP,转后的端口也相同。 但事实上,并不是所有的 NAPT 都能满足这个条件。

NAT 类型

NAPT 总共有 4 中类型,分别是:

NAPT 类型别称端口转换策略将外网设备消息转发给内网设备 A 的条件
Full Cone NATNAT1所有来自同一 个内网设备的请求均被转换为同一端口A 曾经用该端口向外部任意地址发送过消息
Restricted Cone NATNAT2所有来自同一 个内网设备的请求均被转换为同一端口A 曾经用该端口向消息的发送地址发送过消息
Port Restricted Cone NATNAT3所有来自同一 个内网设备的请求均被转换为同一端口A 曾经用该端口向消息的发送地址与发送端口发送过消息
Symmetric NATNAT4只有来自于同一个内网设备、针对同一目标的请求才被转换为同一端口。来自同一 个内网设备、针对不同目标的请求会被转换为不同端口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

整理一下,给出每个代理服务器的任务:

转发机监听端口
主机 A23333、8888、8888 端口消息的发送端口 p
主机 B34444、7777、7777 端口消息的发送端口 p
服务器34444、8888、8888 端口消息的发送端口 p
转发机转发逻辑
主机 A23333-> 服务器:34444;8888-> 手机 A:7777;p-> 服务器:p;
主机 B34444-> 内网广播:23333;7777-> 服务器:8888;p-> 手机 B:p;
服务器34444-> 主机 B:34444;8888-> 主机 A:8888;p-> 主机 B:p;

结语

方案一虽然不可行,但是通过对方案一的可行性分析,学习了 NAT 与 UDP 穿透的相关知识,也算收获匪浅。方案二虽然可行,但是因为要持续占有服务器资源,所以如果将此方案列为第二方案。方案二是假设 A 和 B 两个玩家的联机情况,我的想法是,先完成双人联机的情况,再此基础上再逐步扩展。转发机我打算用 Java 实现,因为 Java 网络与多线程的编程封装了很多实用的 API,且 Java 具有良好的移植性

完善转发机的转发规则

前言

在上一篇中,我通过表格对转发机的监听端口和转发逻辑进行了简短的描述。

转发机监听端口
主机 A23333、8888、8888 端口消息的发送端口 p
主机 B34444、7777、7777 端口消息的发送端口 p
服务器34444、8888、8888 端口消息的发送端口 p
转发机转发逻辑
主机 A23333-> 服务器:34444;8888-> 手机 A:7777;p-> 服务器:p;
主机 B34444-> 内网广播: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主机 A23333服务器34444
2服务器34444主机 B34444
3主机 B34444局域网广播23333
4主机 B7777服务器8888
5服务器8888主机 A8888
6主机 A8888手机 A7777
7主机 Ap服务器p
8服务器p主机 Bp
9主机 Bp手机 Bp

为着重关注主机 A、B 与服务器的交互问题,将上述规则按交互对象进行分组,每组规则按时序升序排列。即 1、5、6、7 作为主机 A 与服务器交互的一组,2、4、8、9 作为主机 B 与服务器交互的一组,完成后表格如下:

时序源地址监听端口源端口目的地址目的端口
1主机 A23333服务器34444
5服务器8888主机 A8888
6主机 A8888手机 A7777
7主机 Ap服务器p
时序源地址监听端口源端口目的地址目的端口
2服务器34444主机 B34444
3主机 B34444局域网广播23333
4主机 B7777服务器8888
8服务器p主机 Bp
9主机 Bp手机 Bp

先考虑未知端口数 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主机 A23333服务器34444
5服务器8888主机 A8888
6主机 A88889898手机 A7777
7主机 A9898服务器9999
时序源地址监听端口源端口目的地址目的端口
2服务器34444主机 B34444
3主机 B34444局域网广播23333
4主机 B7777服务器8888
8服务器9999主机 B9999
9主机 B99997777手机 Bp

随后开始针对 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主机 A233338888服务器34444
5服务器888834444主机 A8888
6主机 A88889898手机 A7777
7主机 A98989999服务器9999
时序源地址监听端口源端口目的地址目的端口
2服务器3444434444主机 B34444
3主机 B3444434444局域网广播23333
4主机 B77779999服务器8888
8服务器99998888主机 B9999
9主机 B99997777手机 Bp

结论

为方便 Java 面向对象的编程设计,将上述规则,按源地址进行分类:

主机 A

监听端口源端口目的地址目的端口
233338888服务器34444
88889898手机 A7777
98989999服务器9999

注:第二行源端口、第三行监听端口应该一致,这里设置为 9898,其实可以设为成任何值,一致就行,不建议设置已经出现过的监听端口,不方便进行本地测试。

主机 B

监听端口源端口目的地址目的端口
3444434444广播23333
77779999服务器8888
99997777手机 Bp

注:p 等于 7777 端口所收到的报文的源端口,应在接受到 7777 端口的消息时动态设定。

服务器

监听端口源端口目的地址目的端口
3444434444主机 B“ 34444”
888834444主机 A“8888”
99998888主机 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实现的元气骑士公网异地联机游戏设计的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/169586

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu