• 游戏编程系列[1]--游戏编程中RPC协议的使用


       RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。

       首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。

    以上摘自,百度百科。

       第一个问题是Why?通过一个特定协议定义,我们可以脱离常规的协议定义。使用一个通用协议来达到远程调用的过程。
       第二个问题协议?我们选择的是Json协议,好处就是灵活多变,可以使用一个字符串表示所有对象。
          坏处就是解析略慢,因为没有长度标识,所以不能搜索,只能一次性解析。不过在于交换场景,这个一点问题都没。

    这里是协议定义:

    请求定义:

        public partial class RequestObj
        {
            /// <summary>
            /// 命令(一般为方法名称)
            /// </summary>
            public string cmd { get; set; }
    
            /// <summary>
            /// 名字空间
            /// </summary>
            public string ns { get; set; }
    
            /// <summary>
            /// 参数
            /// </summary>
            public object[] args { get; set; }
    
            /// <summary>
            /// 请求序号
            /// </summary>
            public int cid { get; set; }
    
            /// <summary>
            /// 如果WebSocket才使用
            /// </summary>
            public string tk { get; set; }
    
            /// <summary>
            /// 主动版本同步,0则忽略
            /// </summary>
            public int ver { get; set; }
    
    	}

    响应定位:

      /// <summary>
        /// 返回数据
        /// </summary>  
        public partial class ResponseObj 
        {
            /// <summary>
            /// 返回数据对象
            /// </summary>
            public object result { get; set; }
    
            /// <summary>
            /// 是否出错信息
            /// 默认0 无出错信息
            /// </summary>
            public int error { get; set; }
    
            /// <summary>
            /// 请求序号
            /// </summary>
            public int cid { get; set; }
    
            /// <summary>
            /// 差异数据
            /// </summary>
            public OpChangeItem[] opstr{ get; set; }
    
        }

    疑问:
    object,这是一个不科学的设定?这边待保留。
    OpChangeItem,这个是什么鬼?依然保留。

    这里我们尝试来进行一次调用构造。

    特定方法:

    	public class LocalTest
    	{
    			/// <summary>
    			/// 添加一个消息
    			/// </summary>
    			public static void AddMessage(string topic, string messageBody)
    			{
    				//这里什么都不干
    			}
    	}

    发送消息:

    {"args":["Test","Message"],"cid":"1","cmd":"AddMessage","ns":"LocalTest","tk":null}

    从以上我们可以解析出来

    类名:LocalTest, 调用方法名:AddMessage,参数[topic : Test , messageBody:Message]

    通过反射调用,我们就完成了执行的目的。

    然后是返回值:

    调用完成后,返回消息:

    {"result": null , "error" : 0 ,"cid" : 1, "opstr":[] }

    注:cid的设定是为了区分调用的请求源,如果你有多个调用同时发起,那么cid拿来区分你的来源,从而达到返回值的匹配。

    从以上例子看出,我们调用了一个AddMessage方法,服务端进行了响应,然后什么事情都没发生。

    我们这里的协议,抛开Opstr字段已经可以完整的描述一整个调用,因为返回值,和参数都用的object,
    所以只要知道类型,那么可以通过json转换成对应的参数和类型,所以我们无需特别约定协议即可以得到特定的类型。

    然后我们进行修改

    		public void AddMessage(string topic, string messageBody,Action callback)
            {
                try
                {
    				//这边进行一次封装,直接把当前的方法调用包装成请求,然后通过标记,判是否是本地执行,
    				//如果是本地执行,则执行本地
                    var __cid = WindNet.RemoteCall.Instance.DoCall(callback, this, "LocalTest.AddMessage", topic,messageBody);
                    if (WindNet.RemoteCall.Instance.IsLocal || __cid.isLocal)
                    {
    				   //原本执行的事情
                       //这里什么都不干
    				   if(callback!=null)
    						callback();
                    }
    				//如果是远程调用,则什么都不干。
                }
                catch (Exception)
                {
                    WindNet.RemoteCall.Instance.DoError(e);
    				throw;
                }
            }
    

      

    首先我们加了个callback参数,当执行完毕之后,我们调用一下callback
    因为网络是一个不稳定的玩意,如果你一直在等待,则可能卡住主线程,在游戏中你的渲染就无法进行,那么我们使用callback来进行流程处理。
    以及返回值的处理。
    流程就是这样,你调用函数-》构造请求-》发起请求-》 服务器返回后-》调用callbak
    如果我们把IsLocal 设为true
    流程就是
    你调用函数-》构造请求(不发出)-》执行本地方法-》执行callback

    经过我们的设计,我们在执行函数前进行了一次简单的拦截,然后通过通用协议和cid进行callback的匹配,然后达到正确的回调过程。

  • 相关阅读:
    qt5--创建控件的两种方式
    qt5-编码转换
    C++qt助手assistant
    C++opencv绘制几何图形
    C++opencv创建图像
    【全球软件大会】华为前端工程师分享:华为云官网的智能化实践
    图解 Redis丨这就是 RDB 快照,能记录实际数据的
    云小课 | 玩转HiLens Studio之快速订购HiLens Studio版本
    带你认识4种设计模式:代理模式、装饰模式、外观模式和享元模式
    线性表、顺序表和链表,你还分不清?
  • 原文地址:https://www.cnblogs.com/icesun963/p/6251646.html
Copyright © 2020-2023  润新知