Unity Mirror 之NetworkDiscovery局域网服务器查找详解(1)

1,562 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情

序言

使用Mirror的NetworkManager时,作为客户端要加入服务器,需要输入服务器的地址,而在局域网中联机时,服务器由客户端兼任,即Host模式。并且由于没有一个中心游戏大厅服务器的存在,每个客户端都不知道当前已启动的Host主机的地址。因此Mirror提供了NetworkDiscovery组件来发现局域网中的服务器。该组件的原理是通过UDP广播去探测局域网中存在的Host。而作为Host的主机必须启动一个UDP客户端去响应来自游戏客户端的广播消息,并对于发送广播的客户端返回握手包,这样客户端在接收到握手包之后,即发现了该主机。一般握手包中会带有Host主机的地址,客户端使用该地址即可连接入该主机。之前的文章,Unity Mirror 之 NetworkDiscovery原理中已经对NetworkDiscovery的原理进行了讨论,本文则从实践出发,展示该组件的具体使用方法和需要注意的问题。

NetworkDiscovery的基础用法

添加NetworkDiscovery组件

首先,NetworkDiscovery组件是客户端和服务器共用的,它即有客户端UDP广播的功能也有服务器接收广播并返回握手包的功能。而服务器在开始游戏的同时会开启UDP服务,开始游戏一般意味着切换场景。所以NetworkDiscovery组件需要在切换场景后仍然存在。一个简单的做法是将它添加到NetworkManager的GameObject上,因为NetworkManager是跨场景全局存在的。

客户端操作

首先,客户端需要获得一个NetworkDiscovery组件的引用并保存

public NetworkDiscovery networkDiscovery;

然后客户端还需要维护一个查找到的服务器的字典

readonly Dictionary<long, ServerResponse> discoveredServers = new Dictionary<long, ServerResponse>();

这个代码只是举个例子,服务器信息可以保存在自定义的数据结构里面。另外这个字典的key是ServerResponse.serverId,这个serverId是啥,为什么需要serverId,后面会单独讲。这儿只需要理解为服务器的标识即可。

进入服务器列表页面后

networkDiscovery.StartDiscovery();

使用该方法开始发送广播包,搜索服务器。

收到服务器握手包后的回调

public void OnDiscoveredServer(ServerResponse info)
{   
    discoveredServers[info.serverId] = info;
    //刷新UI
}

OnDiscoveredServer是添加到NetworkDiscovery组件的OnServerFound这个UntyEvent上的事件处理器。 需要注意的是,这个事件是不断的被调用的,因为他仅仅是服务器对于广播包的回应。由于广播包是持续发送的,默认3秒一次,那么对于同一个服务器,也必然是3秒一次的发送回应,导致这个事件也3秒调用一次。另外如果存在多个服务器,比如N个,那么这个事件就是3秒调用N次。所以使用该事件刷新UI时要注意到这点,要判断当前的事件对应的服务器是否已经存在,如果不存在就给它分配一个UI并展示,如果已经存在可以再刷新一次内容(有可能服务器信息会变,比如对于自定义的服务器消息可能返回当前的人数,这个是会变化的)。

连接到服务器

客户端找到一个或多个服务器后,就可以使用以下方法连接到服务器进行游戏了。

void Connect(ServerResponse info)
{
    networkDiscovery.StopDiscovery();
    NetworkManager.singleton.StartClient(info.uri);
}

Connect方法中,首先调用了networkDiscovery.StopDiscovery();因为客户端已经连接服务器了,不再需要查找服务器,因此调用这个方法停掉UDP客户端以及定时广播。然后使用StartClient,以SeverResponse.uri为参数,进入游戏。注意这儿的 info.uri 是经过NetworkDiscovery组件的处理之后得到的,包含了正确的Host地址。

服务器端操作

启动游戏主机时

启动UDP客户端去等待UDP广播。

discoveredServers.Clear();
NetworkManager.singleton.StartHost();
networkDiscovery.AdvertiseServer();

AdvertiseServer这个方法内部就是启动了一个UDP客户端,等待客户端的广播包并且处理返回握手包。这个方法里面会调用StopDiscovery来停掉客户端的UDP和广播,因为对于服务器这些是不需要的。这儿我想吐槽一下Mirror的这套NetworkDiscovery,把客户端和服务器糅合到一起其实并不好,不清晰,应该分成两个组件。比如这儿的StopDiscovery,其实里面会同时停掉客户端和服务器的UDP。

主机关闭时

也要记得调用StopDiscovery。