缘起

因为在浙大,物理网卡的地址被分配为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

制作一个自己的对战平台相关推荐

  1. 使用 Vue.js 制作一个简单的调查问卷平台

    使用 Vue.js 制作一个简单的调查问卷平台 原文  https://github.com/pramper/Demos/tree/master/Vue-Demos/Questionnaire 主题  ...

  2. 制作一个二人对战坦克游戏

    //此脚本用于网络连接和创建玩家 using UnityEngine; using System.Collections; using UnityEngine.UI; using UnityEngin ...

  3. 用JS制作一个信息管理平台完整版

      前  言 JRedu 在之前的文章中,介绍了如何用JS制作一个实用的信息管理平台. 但是那样的平台功能过于简陋了,我们今天来继续完善一下. 首先我们回顾一下之前的内容.   1.JSON的基础知识 ...

  4. 55节开源巨献,教你制作一个智能无线电应答平台

           <如何制作一个智能无线电应答平台>                                             作者:BG7EJL 项目背景 目前市面上基于U/V ...

  5. cocos2d-x 如何制作一个类马里奥的横版平台动作游戏 1 献给所有对动作游戏有爱的朋友

    本文翻译自国外著名IOS源码教学商业网站raywenderlich 的 IOS Game Start Kits三件套之一的Platformer Game/平台动作游戏的前奏曲,另一个是Beat'Em ...

  6. cocos2d-x 如何制作一个类马里奥的横版平台动作游戏 1 献给所有对动作游戏有爱的朋友...

    本文翻译自国外著名IOS源码教学商业网站raywenderlich 的IOS Game Start Kits三件套之一的Platformer Game/平台动作游戏的前奏曲,另一个是Beat'Em u ...

  7. 如何在完全不懂服务器开发的情况下做一个实时联网对战的微信小游戏

    微信小游戏即将开放?有我们在,你还赶得上! 根据微信官方对外公开的消息,微信小游戏的脚步越来越接近了.它的开发者资格门槛和使用者门槛都很低,以后必将引爆一波"全民开发小游戏"浪潮. ...

  8. ios一个app调起另一个app_电商app开发价格:制作一个电商app需要多少钱?

    智能手机的发展,带动了各式各样手机app的市场,现在大家网购大多数都是通过电商app实现,再加上分销.配送等模式发展,自建电商app成为很多企业的选择,电商app开发成本大概多少?制作一个电商app需 ...

  9. 怎么用python制作简单的程序-神级程序员教你如何用python制作一个牛逼的外挂!...

    玩过电脑游戏的同学对于外挂肯定不陌生,但是你在用外挂的时候有没有想过如何做一个外挂呢?(当然用外挂不是那么道义哈,呵呵),那我们就来看一下如何用python来制作一个外挂.... 我打开了4399小游 ...

最新文章

  1. AI一分钟 |世界上第一个无人驾驶出租车在迪拜投入使用,2030年无人驾驶将覆盖迪拜25%的交通行程
  2. Python3.5源码分析-内存管理
  3. java类加载器、双亲委派、沙箱安全机制全都让你整明白(三万字,收藏慢慢啃)
  4. 看完Andoird9.0 Pie的隐藏特性,我买了SSL证书
  5. 15 MM配置-BP业务伙伴-定义供应商主记录的编号范围
  6. 苏宁买买买!将收购家乐福80%股份 成为家乐福中国控股股东
  7. 自学python要看哪些书-学习机器学习应该看哪些书籍?
  8. R包实践:lubridate 处理时间数据
  9. php emoji处理微信表情
  10. 六石管理学:半弹性工作时间
  11. MySql修改默认端口
  12. 电子学会2021年3月青少年软件编程(图形化)等级考试试卷(二级)答案解析
  13. 猿人学web端爬虫攻防大赛赛题解析_第九题:js混淆-动态cookie2
  14. HTML在线颜色选择器源码
  15. 上传文件资料并生成缩略图
  16. LVM修复-误删除磁盘
  17. IDCE-CT系列蓄电池放电容量测试仪(蓄电池容量检测仪/放电仪)功能介绍
  18. windows开源工具大全
  19. rust睡觉按键没反应_腐蚀Rust实用技巧大全 Rust新手上手指南
  20. 彩虹网盘外链程序网站源码V5.1

热门文章

  1. c语言贪吃蛇大作业报告,C语言贪吃蛇实验报告
  2. 电脑中毒了老是自动安装软件怎么办
  3. 防止系统内存溢出触发OOM的一个内核参数
  4. matlab取实部_matlab 计算行列式 出现复数解 无法使用real来获取实部? - 计算模拟 - 小木虫 - 学术 科研 互动社区...
  5. 蓝桥杯电子类单片机组模块——led显示(一般作用)
  6. PAT乙级刷题心得和常用函数总结 (c++实现)
  7. 满满的干货!传统备份vs CDP vs CDM
  8. 图片去底色,图片透明化,免费简单快捷 [ 没有比这更好用的了 ]
  9. 卸载金蝶kis记账王的方法
  10. Windows系统盘清理