26
2025
03
21:09:00

【Mirror Networking】网络框架源码学习

前言

Unity5.1为开发者发布全新的多玩家在线工具、技术和服务。该技术的内部项目名称为 UNET,全称为 Unity Networking。这个方案已经被官方弃用了。不进行更新了
但现在项目有用到UNET(现在换成了Mirror网络框架,UNET的替代品)
还是要学习一下网络框架才能更好学习项目内容。我们直接学习Mirror!

1. Mirror介绍

  • Mirror是一个为Unity 2019/2020 LTS及以上版本设计的高层网络框架,兼容不同底层传输接口.

  • Mirror的设计就是为了简单易用,快速上手。封装网络底层,一线开发人员可以不需要了解底层的网络传输结构。

  • 允许同时作为客户端或服务器端,所以不需要搭建专用的服务器。

【Mirror Networking】网络框架源码学习

2. 网络通信原理

1. CMD和RPC是干嘛用的?

Mirror的特性,使得客户端服务端逻辑都在一块,那么我们需要一些标签来区别客户端服务端代码。

  • [Server]/[Client]:用来区别服务端/客户端专用代码

  • [Command]:用来标记客户端对服务端的远程调用。

    • 底层原理是通过事件调用Cmd 方法,把数据传到 Server 端

  • [ClientRpc]/[TargetRpc]:远程过程调用(Remote Procedure Calls) ,简称RPC

    • RPC用来调用远程计算机上某个函数的方法,这个分为2种:

    • 用来标记服务器端调用所有客户端[TargetRpc]

    • 或者某个特定客户端(ClientRpc)的函数。

    • 底层也是事件机制。

  • [SyncVar]:用来标记从服务端同步到客户端的变量。

    • 这种特性的数据的修改只能从服务端修改,客户端是没有权限修改的,只能读取。要通过有[ServerCallBack],[Server]的方法修改。

    • 其实就是不断分发玩家的状态数据,这个过程消耗大量的带宽,所以我们要优化带宽数量。

3. Examples/Chat

打包出来,用一个端当服务器,其他端连接 localhost 当客户端,就能达到以下效果

【Mirror Networking】网络框架源码学习

  • 核心就是点击按钮会调用CmdSend,然后就大家都看见了别人发的消息内容?

  • why?

  • 接下来学习一下Command和ClientRpc 参考:通讯部分:Remote Actions

        [Command]
        public void CmdSend(string message)
        {
            if (message.Trim() != "")
                RpcReceive(message.Trim());
        }        [ClientRpc]
        public void RpcReceive(string message)
        {
            OnMessage?.Invoke(this, message);
        }

4. Command 实现原理源码学习

  • 命令是从客户端发送到服务器上的一个函数。要把一个函数变成一个命令,只要加上[Command]修饰符,也可以加上Cmd前缀来区分。函数变成命令后,就可以在服务器上运行

  • 并且命令不能被static修饰

那么这么神奇的Command是什么实现的呢?

    [AttributeUsage(AttributeTargets.Method)]
    public class CommandAttribute : Attribute
    {
        public int channel = Channels.Reliable;
        public bool requiresAuthority = true;
    }

Command 属性修饰方法,他生效分成2个部分,①编译后处理;②服务器消息监听

①编译后处理:

  • 找一下 CommandAttribute 的引用,就会发现如下堆栈。

  • 在代码编译结束后,Mirror 会紧锣密鼓的对各个自定义属性进行进行处理,堆栈如下:

CompilationFinishedHook.OnCompilationFinished
  Weaver.WeaveAssembly
    Weaver.Weave
      Weaver.WeaveModule
        Weaver.WeaveNetworkBehavior
          NetworkBehaviourProcessor.Process
            NetworkBehaviourProcessor.ProcessMethods
              NetworkBehaviourProcessor.ProcessCommand\ProcessTargetRpc\ProcessClientRpc 
              生成 CmdThrust 方法,添加到 commandInvocationFuncs 委托列表里面

NetworkBehaviourProcessor.GenerateConstants
  NetworkBehaviourProcessor.GenerateRegisterCommandDelegate/GenerateRegisterRemoteDelegate/GenerateSyncObjectInitializer
  生成IL代码版本的 委托注册
  • ProcessCommand 会校验方法和参数有效性(方法不支持泛型、不支持返回值,不支持IEnumerator、Command 方法不能重复)

  • 编译后,会生成 IL 代码,生成一个 CmdThrust 方法,最终调用 NetworkBehaviour 的SendCommandInternal 方法

【Mirror Networking】网络框架源码学习

②服务器消息监听

  • 服务器在启用的时候,会注册监听 OnCommandMessage,然后由 identity 去处理远程调用

    • identity.HandleRemoteCall

      • RemoteCallHelper.InvokeHandlerDelegate

③客户端调用

  • 客户端调用带有 Command 标签的方法,会调用到 SendCommandInternal 方法,向服务端发送消息。

  • 服务端通过IL层注册的委托,直接找到对应的方法,进行调用。

  • 这样就实现了远程过程调用。

TargetRpc 和 ClientRpc 原理应该也是一样的,只是调用方向反过来了而已,就不赘述了

5. Examples/Basic 简单食用

打包出来,用一个端当服务器,其他端连接 localhost 当客户端,就能达到以下效果

【Mirror Networking】网络框架源码学习

我对这个案例比较好奇的点就是,这些数据是怎么同步的?SyncVar是怎么实现的。

6. SyncVar 实现源码学习

  • SyncVar 用来标记从服务端同步到客户端的变量。就是个 ClientRPC 操作。

SyncVar 在函数中是这样定义的:

    [AttributeUsage(AttributeTargets.Field)]
    public class SyncVarAttribute : PropertyAttribute
    {
        public string hook;
    }

SyncVar实现数据同步分成2部分:①编译后处理 SyncObject初始化;②Update驱动同步

①编译后处理 SyncObject初始化

// 生成 SyncVar 在 IL 的 Get\Set 方法
CompilationFinishedHook.OnCompilationFinished
  Weaver.WeaveAssembly
    Weaver.Weave
      Weaver.WeaveModule
        Weaver.WeaveNetworkBehavior
          NetworkBehaviourProcessor.Process
            NetworkBehaviourProcessor.ProcessSyncVars
              SyncVarProcessor.ProcessSyncVar:生成IL的Get\Set方法


  1. 注释说,会在构造Weaver的时候调用 InitSyncObject ,收集所有同步对象,然后在OnSerialize/OnDeserialize 的时候进行同步更新

【Mirror Networking】网络框架源码学习

SerializeSyncVars,初始化所有 SyncVar,然后逐帧更新 dirty 的值

【Mirror Networking】网络框架源码学习

②NetworkServer Update 驱动同步

NetworkServer.NetworkLateUpdate
  NetworkServer.Broadcast
    NetworkServer.BroadcastToConnection
      NetworkServer.GetEntitySerializationForConnection
        NetworkIdentity.GetSerializationAtTick
          NetworkIdentity.OnSerializeAllSafely
            NetworkIdentity.OnSerializeSafely
              NetworkBehaviour.OnSerialize
  • NetworkServer 的 NetworkLateUpdate 里有个 Broadcast 广播方法

  • 在广播里,我们会不断去向已经准备好的端,广播序列化好的数据。

【Mirror Networking】网络框架源码学习

7. 服务器监听部分 源码学习。以 KcpTransport 为例子

①初始化绑定

  • NetworkManager.SetupServer

    • NetworkServer.Listen

      • Transport.activeTransport.ServerStart

        • KcpServer.Start:新建 UDP socket,绑定IP(NetworkManager.networkAddress)和端口(activeTransport.port)

        • 以 KcpTransport 为例子。这个 Transport 是可以传入的,这边用的 KcpTransport。见下图

【Mirror Networking】网络框架源码学习

②Update Tick收到的数据

NetworkLoop.RuntimeInitializeOnLoad
  NetworkLoop.NetworkEarlyUpdate
    NetworkServer.NetworkEarlyUpdate
      以 KcpTransport 为例子 KcpTransport .ServerEarlyUpdate
        KcpTransport.TickIncoming
          KcpServer.ReceiveFrom
            KcpServer.OnData.Invoke(connectionId, message);
              NetworkServer.OnTransportData
                NetworkServer.UnpackAndInvoke
                  handler.Invoke(connection, reader, channelId); 根据消息类型,找到对应的委托,进行调用

③业务部分

  • 以一个 ReadyMessage 为例子:

  • 服务端注册 ReadyMessage 委托

    • RegisterHandler<ReadyMessage>(OnClientReadyMessage);

  • 客户端发送 ReadyMessage

    • connection.Send(new ReadyMessage());

  • 这样服务端收到消息,就能找到对应委托进行调用

总结

  • 总结一下,就是编译后处理,生成IL代码去调用 NetworkBehaviour 的 SendCommandInternal

  • 服务器在启用的时候,注册消息监听

  • 就这样悄摸摸地藏好了代码,让我们去摸不着头脑,找不着调用

  • 在我看来[Command]就是语法糖,封装了重复,机械的消息注册,消息接收等逻辑到IL层。

参考

Unity3D-network网络相关(一)_alayeshi的专栏-CSDN博客

Unity3D-network网络相关(二)_alayeshi的专栏-CSDN博客

交大计算机课程(5):计算机网络

GitHub - vis2k/Mirror: #1 Open Source Unity Networking Library

Mirror Documentation

Unity 使用Mirror框架制作多人游戏

MirrorNetworking




推荐本站淘宝优惠价购买喜欢的宝贝:

【Mirror Networking】网络框架源码学习

本文链接:https://hqyman.cn/post/9795.html 非本站原创文章欢迎转载,原创文章需保留本站地址!

分享到:
打赏





休息一下~~


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

请先 登录 再评论,若不是会员请先 注册

您的IP地址是: