携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情
NetworkDiscovery找不到主机?
在使用Mirror的NetworkDiscovery时,经常会发现找不到主机,这是因为Mirrro的默认实现有一些问题,需要修改。
局域网UDP广播问题
使用Mirror的NetworkDiscovery查找服务器时,会发现找不到主机的情况。后来发现是由于在局域网中使用UDP广播的问题。在Mirror中,UDP广播使用的地址是IPAddress.Broadcast,即"255.255.255.255",这是物理层的广播地址。如果两个设备在同一个交换机下是没问题的,但是如果他们被隔离了,这个广播包是不会被路由器转发的,即便他们属于同一个子网,也不能接收到对方发出的广播包。
解决方法
解决的方法是不要使用255.255.255.255
这个广播地址,而是使用子网广播地址。比如你的网段是192.168.1.0/24
,那么该子网的广播地址就是192.168.1.255
。但是,你不能直接在代码里面写死192.168.1.255
,因为你不知道玩家的局域网是什么样的,比如可能是192.168.0.0/16
,虽然不常见,但是也是有可能的。所以我们需要分析客户端的网络地址,并计算出相应的子网广播地址。我们需要修改的地方是NetworkDiscoveryBase.BroadcastDiscoveryRequest
方法:
IPEndPoint endPoint = new IPEndPoint(IPAddress.Broadcast, serverBroadcastListenPort);
这儿使用的是IPAddress.Broadcast
, 说实话如果对于小白来说,不知道这个地址可能发送不到也就算了,Mirror的作者都是长期开发网络游戏的,居然没发现这个问题?可能是因为他们不太关注局域网联机吧。好了,我们需要的修改就是将这儿的广播地址替换成当前网段的广播地址。这儿我们先复习一下网段和IP地址吧。比如192.168.1.2这个IP,所属的网段为192.168.1.0/24,子网掩码为255.255.255.0,对于这样的网段,192.168.1.255就是它的广播地址。所以这儿的事情就是首先找到本机的IP,然后分析这个IP,最后计算出网段的广播地址。这些操作还是比较麻烦的,我整理了一下,可以直接用:
using System;
using System.Net;
using System.Net.Sockets;
public static class IPAddressExtensions
{
public static IPAddress GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip;
}
}
return IPAddress.None;
}
public static IPAddress GetBroadcastAddress(this IPAddress address)
{
IPAddress subnetMask = address.GetSubnetMask();
byte[] ipAdressBytes = address.GetAddressBytes();
byte[] subnetMaskBytes = subnetMask.GetAddressBytes();
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
for (int i = 0; i < broadcastAddress.Length; i++)
{
broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
}
return new IPAddress(broadcastAddress);
}
public static IPAddress GetSubnetMask(this IPAddress address)
{
uint firstOctet = GetFirtsOctet(address);
string subnetMask = "0.0.0.0";
if (firstOctet >= 0 && firstOctet <= 127)
{
subnetMask = "255.0.0.0";
}
else if (firstOctet >= 128 && firstOctet <= 191)
{
subnetMask = "255.255.0.0";
}
else if (firstOctet >= 192 && firstOctet <= 223)
{
subnetMask = "255.255.255.0";
}
return IPAddress.Parse(subnetMask);
}
private static uint GetFirtsOctet(IPAddress iPAddress)
{
byte[] byteIP = iPAddress.GetAddressBytes();
uint ipInUint = (uint)byteIP[0];
return ipInUint;
}
}
有了这个类后,上面广播的地方这样改就可以:
var broadcastAddress = IPAddressExtensions.GetLocalIPAddress().GetBroadcastAddress();
IPEndPoint endPoint = new IPEndPoint(broadcastAddress, serverBroadcastListenPort);
WIFI网络问题
笔者在测试UDP广播时发现,对于移动设备,或者PC使用了无线网卡,做主机时会有一些奇怪的现象。首先是广播包迟迟接收不到,可能得很长时间才收到。然后是接受到广播包并发送回包(log是正常发送的),但是这些回包客户端收不到的。另外如果移动设备正好待机了,就会积压一些包,等恢复的瞬间发出大量的包,客户端就有可能收到。这个问题困扰了我两三天,各种方法都试过了都不好使,后来我确定应该不是我这边的问题,可能是网络环境的问题,换了一个路由器后就全正常了!我猜测之前使用的路由器对于UDP广播包有限制吧,可能和固件有关系,那个固件也是一个修改版的固件。