携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情
问题处理
主机断开后没有通知?
前一篇中的流程有一个主要的问题是,主机断开(关闭),客户端并不会收到任何通知,如果之前客户端已经搜索到该主机,那么该主机就一直停留在客户端的服务器列表上。由于UDP本来就是无状态的,你只知道之前有过,但是后来就一直没有了。所以我给出的方法就是记录每次服务器回包的时间,如果一段时间后离上次回包的时间大于某个阈值,则可以认为服务器已经关闭或者断开了。这样就在UI上把他去掉。
NetworkDiscovery的一些细节
关于 serverId
注意到NetworkDiscovery返回的服务器消息 ServerResponse 中包含了 serverId,并且这个serverId用作客户端服务器字典的key。我们先看一下这个serverId的注释:
Prevent duplicate server appearance when a connection can be made via LAN on multiple NICs
意思是对于UDP客户端,如果服务器上有多个网络接口(NIC),比如多个网卡,由于广播消息是不指定IP的,所以这多个网卡都会收到广播消息,因此通过UDP返回的包里面,是包含不同的源IP的。这样对于这种服务器,客户端那边就有可能收到来自于一个服务器的多个回包。为了避免客户端的服务器列表UI上出现重复的服务器条目,因此Mirror推荐对于每个服务器给予一个唯一ID。但是这唯一ID并不是唯一的,而是使用了一个随机数,当然从概率上说很难重复:
ServerId = RandomLong();
关于 ServerResponse.uri
ServerResponse中的uri其实在构建出ServerResponse时是不能用的,因为它的Host不一定是IP地址。当服务器返回握手包时,这uri是来自NetworkManager底层的transport的:
return new ServerResponse
{
serverId = ServerId,
uri = transport.ServerUri()
};
问题是,这个uri的Host并不是服务器的IP地址。我使用的是KcpTransport,是这么实现的:
public override Uri ServerUri()
{
UriBuilder builder = new UriBuilder();
builder.Scheme = Scheme;
builder.Host = Dns.GetHostName();
builder.Port = Port;
return builder.Uri;
}
这儿Dns.GetHostName()返回的是A string that contains the DNS host name of the local computer.
,即电脑的名字,所以这个uri不能用来连接服务器。因此在NetworkDiscovery内部,ProcessResponse函数中进行了处理。ProcessRequest函数是一个定义在NetworkDiscoveryBase
中的虚函数,用于对服务器生成的握手包进行进一步的处理(在ReceiveGameBroadcastAsync中调用)。
protected override void ProcessResponse(ServerResponse response, IPEndPoint endpoint)
{
// we received a message from the remote endpoint
response.EndPoint = endpoint;
// although we got a supposedly valid url, we may not be able to resolve
// the provided host
// However we know the real ip address of the server because we just
// received a packet from it, so use that as host.
UriBuilder realUri = new UriBuilder(response.uri)
{
Host = response.EndPoint.Address.ToString()
};
response.uri = realUri.Uri;
OnServerFound.Invoke(response);
}
这个函数里面,将uri的Host替换成了EndPont的Address,这是一个真实的IP。 关注这些细节(包括serverId)是为了我们自己实现自定义的NetworkDiscovery时避免出问题。