制作一个自己的对战平台

news/2025/2/12 3:15:58/

缘起

因为在浙大,物理网卡的地址被分配为222.205.XX.XX,但是子网掩码是255.255.255.0,这样的话虽然大家都在一个局域网里面,但是却不一定在同一个子网。
局域网联机游戏为了发现局域网中的主机,会发送广播包,有些局域网联机游戏,会发送到255.255.255.255这个广播地址(典型代表War3),但是这个广播地址
是只能广播到子网的,路由器默认不转发,这样就造成了我们同在校园网却无法联机的问题。

虚拟局域网

想了想解决方案,可能使用虚拟网卡做一个虚拟局域网是一个解决方案,于是我安装了OpenVPN,然后使用其tap0901网卡驱动,可以读取注册表
获取tap0901设备的实例UUID以及显示在网络和共享中心的那个网络名称:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}
这个里面有很多项,代表了多个网卡接口,其中会有一个是tuntap设备,通过类似这样的地址HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}\0002下的ComponentIdtap0901来确定tap网卡,然后读取该项下面的netCfgInstanceId,就是tap网卡的UUID,得到这个UUID之后,
可以使用CreateFile函数来打开一个tuntap设备:

HANDLE f = CreateFile(L"\\\\.\\Global\\{...}", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, NULL);

打开这个设备后,就可以对其发送指令了,参照tuntap的驱动源代码tap-windows6,可以获得一些宏,以及一些指令的参数信息,然后使用
DeviceIoControl发送到设备:

DeviceIoControl(f, TAP_IOCTL_SET_MEDIA_STATUS, config, 4, config, 4, &returnLen, NULL);

启动tuntap设备之前,要考虑到单机游戏发送广播包,所以应该修改路由表,让255.255.255.255路由经过我们的tuntap设备,以及作为虚拟局域网,每个tuntap设备应该有一个虚拟ip,这里
假设为192.168.1.10。设置IP的windowsAPI在win7之后的系统就没有了,所以我也只能使用调用netsh命令行的方式来设置ip,然后使用GetIpForwardTable2来得到路由表信息,使用
DeleteIpTableEntry2来删除其他路由表,以及通过NotifyRouteChange获得路由表改变的通知,收到后去再次修改路由表,另外在退出时恢复之前的路由表。做好这一步之后,
我们要做的就是转发了。

读取设备上的帧,使用异步读取文件的ReadFile即可:

if (ReadFile(hFile, buff, 1500, &read, &ol) != FALSE) {if (!readHandler(std::string((char*)buff, read))) break;
}
else if ((errNo = GetLastError()) == ERROR_IO_PENDING) {switch (WaitForMultipleObjects(2, handles, false, INFINITE)) {case WAIT_OBJECT_0:if (!readHandler(std::string((char*)buff, ol.InternalHigh))) break;break;case WAIT_OBJECT_0 + 1:return;}
}
else {if (errorHandler != nullptr)errorHandler(errNo);break;
}

这段代码因为处于子线程中,所以还有等待线程退出信号的部分,用了WaitForMultiObjects。我使用的是tap模式,是二层设备,收到的是以太网帧,所以要有以太网帧的解析:

bool EthernetIIPacket::Parse(const char* raw, int length) {if (length < 14) return false;memcpy(this->srcMac, raw + 6, 6);memcpy(this->dstMac, raw, 6);this->protocol = Protocol(ntohs(*(unsigned short*)(&raw[12])));this->userDataLen = length < 1514 ? length - 14 : 1500;memcpy(this->userData, raw + 14, this->userDataLen);return true;
}

然后解析出上层协议类型,如果是IPv4,那么就要对IPv4进行解析:

bool IPv4Packet::Parse(const char* raw, int length) {if (length < 20) return false;this->protocol = Protocol((BYTE)(raw[9]));memcpy(this->srcIp, raw + 12, 4);memcpy(this->dstIp, raw + 16, 4);this->userDataLen = length < 1500 ? length - 20 : 1480;memcpy(this->userData, raw + 20, this->userDataLen);return true;
}

这里只需要解析出我们感兴趣的部分,然后判断,如果是广播地址,那么对所有加入虚拟局域网的设备,使用能够通讯的网卡(这里是校园网VPN)进行通讯,
我需要找到校园网的网卡,使用GetIpForwardTable2找到跃点数最低的默认路由,一定是现在活跃的网络连接,然后再用GetIpAddrTable获得其IP地址,
用来绑定socket到指定网卡。之后就是转发读取到的包了,我这里使用UDP直接将IP包传出去。

之后,为了能够收到其他端从UDP传入的数据,我们需要侦听校园网的IP地址,然后收到包之后将UDP的内容(原始的IP报文)封装到以太网帧中,发送回设备,
但是以太网帧需要知道自己和发送方的MAC地址,这就得使用GetIfTable2来获取网络接口信息了,将tuntap的nac地址填充进去,然后使用WriteFile发送
回设备。

但是这时,我发现主机虽然给后来加入的计算机发送了地图信息,但是之后就没有任何通信了,于是检查了下发包。发现有大量寻找192.168.1.8192.168.1.1
的ARP包(因为测试使用的两台计算机的虚拟IP分别为192.168.1.10,192.168.1.8。原来socket在发送前,会先检索目的IP地址对应的mac是不是在自己mac
表里面,如果不在,那么就通过ARP协议去询问,如果还询问不到,那就去询问网关的,如果搞不定,那么只能无动于衷,也就是不发包了。所以,我又额外实现了一套
ARP协议:

bool IPv4ARPPacket::Parse(const char* data, int length) {if (length < 28) return false;this->opCode = data[7];memcpy(this->senderMac, data + 8, 6);memcpy(this->senderIpAddress, data + 14, 4);memcpy(this->targetMac, data + 18, 6);memcpy(this->targetIpAddress, data + 24, 4);return true;
}

完善ARP协议之后,就可以顺利联机啦!另外注意360可能会报病毒,程序要以管理员身份运行(可以修改链接属性,出现UAC标识),以及关闭windows防火墙
(windows防火墙会拦截不明UDP包,即不请自来的UDP包)。完整代码参见github

![WAR3]


http://www.ppmy.cn/news/162532.html

相关文章

对战平台原理分析

游戏对战平台&#xff0c;在没有了解的情况下&#xff0c;总是给人一种很神秘的感觉&#xff0c;然而&#xff0c;当你对socket的理解到达一定程度之后&#xff0c;你就不会再觉得神秘。 用一句话来总结这种技术&#xff1a;虚拟局域网(VLAN)。 实现这种平台&#xff0c;…

游戏对战平台原理

游戏对战平台&#xff0c;在没有了解的情况下&#xff0c;总是给人一种很神秘的感觉&#xff0c;然而&#xff0c;当你对socket的理解到达一定程度之后&#xff0c;你就不会再觉得神秘。 用一句话来总结这种技术&#xff1a;虚拟局域网(VLAN)。 实现这种平台&#xff0c;主要…

游戏对战平台搭建要选什么服务器

游戏对战平台搭建要选什么服务器 服务器是游戏平台数据传输的重要载体&#xff0c;事关我们游戏创业发展的稳定性、安全性。那么&#xff0c;游戏平台搭建要选什么服务器&#xff1f;有什么参考指标&#xff1f;本文艾西将带领大家一探究竟&#xff01; 首先是“游戏平台搭建要…

m1安装tensorflow踩坑笔记

1.如果安装库报错&#xff1a; PackagesNotFoundError: The following packages are not available from current channels: 检查一下下载的annconda版本是否安成x86 import platform platform.platform()正确输出应该为&#xff1a; ‘macOS-12.6.3-arm64-arm-64bit’ 如果…

技术帖——飞凌嵌入式RK3588开发板推理模型转换及测试

RKNN&#xff08;Rockchip Neural Network&#xff09;是一种用于嵌入式设备的深度学习推理框架&#xff0c;它提供了一个端到端的解决方案&#xff0c;用于将训练好的深度学习模型转换为在嵌入式设备上运行的可执行文件。使用RKNN框架可以在嵌入式设备上高效地运行深度学习模型…

如何实验ChatGPT 提示获取机器学习的数据集

随着机器学习的发展,获取高质量的数据集变得越来越重要。数据集对于评估最终模型的准确性和有效性至关重要,这是任何机器学习项目的先决条件。在本文中,我们将学习如何使用 ChatGPT[OpenAI] 模板提示为不同的机器学习应用程序收集各种数据集,并在 Python 中收集这些数据集。…

i3处理器_办公笔记本i3够用了吗

现在越来越多朋友会为自己办公选用一款办公笔记本&#xff0c;那么这款笔记本的配置是什么&#xff0c;办公笔记本i3够用了吗&#xff1f;下面就为大家带来相关的介绍。 办公笔记本i3够用了吗 1、仅是办公使用i3处理器已经足够&#xff0c;也就是使用的范围主要是文字、表格处理…

笔记本 续航测试软件,PCMark 10新增续航、办公测试:考验笔记本电池

UL(Futuremark)出品的PCMark 10是迄今最完整、最权威的PC综合性能基准测试工具&#xff0c;可以通过各种测试全方位评估一台PC的实际应用性能。 近日&#xff0c;官方宣布PCMark 10将会新增两个测试项目&#xff0c;一是万众期待的电池续航测试(Battery Life Benchmark)&#x…