Socket开发框架之消息的回调处理

在通常的Socket应用里面,不少时候数据的发送和接收是分开处理的,也就是咱们发送一个消息,不知道这个请求消息何时获得应答消息,并且收到对应的应答消息的时候,若是操做界面的内容,也是须要特别处理的,由于它们和界面线程是不在一块儿的。若是咱们在发送消息的时候,可以给一段回调的代码给收到应答消息的时候处理,那么就会方便不少。本文主要介绍如何在Socket应用里面,经过回调函数的处理,实现收到应答消息的时候可以调用对应的函数。服务器

一、回调函数的设计

在上一篇的随笔里面,介绍了基于Json的Socket消息的实体类设计,其中包括了消息回调ID和是否在调用后移除回调两个属性,这个是用来为回调处理服务的,以下所示。函数

也就是在通用消息对象BaseMessage类里面添加下面两个属性。ui

但咱们须要发送消息的时候,咱们把回调的ID添加到本地集合里面,获得应答的时候,在从集合里面提出来,执行就能够了。this

        /// <summary>
        /// 发送通用格式的数据对象
        /// </summary>
        /// <param name="data">通用的消息对象</param>
        /// <returns></returns>
        public bool SendData(BaseMessage data, Delegate callBack = null)
        {
            AddCallback(callBack, data);//添加回调集合

            var toSendData = PackMessage(data);
            var result = SendData(toSendData);

            return result;
        }
        /// <summary>
        /// 记录回调的函数信息
        /// </summary>
        /// <param name="callBack"></param>
        /// <param name="msg"></param>
        private void AddCallback(Delegate callBack, BaseMessage msg)
        {
            if (callBack != null)
            {
                Guid callbackID = Guid.NewGuid();
                ResponseCallbackObject responseCallback = new ResponseCallbackObject()
                {
                    ID = callbackID,
                    CallBack = callBack
                };

                msg.CallbackID = callbackID;
                callBackList.Add(responseCallback);
            }
        }

 

在服务端,须要根据请求的消息构建应答内容,所以咱们在应答请求的时候,须要把请求的回调ID给复制到应答的消息体里面,以下所示。spa

        /// <summary>
        /// 封装数据进行发送(复制请求部分数据)
        /// </summary>
        /// <returns></returns>
        public BaseMessage PackData(BaseMessage request)
        {
            BaseMessage info = new BaseMessage()
            {
                MsgType = this.MsgType,
                Content = this.SerializeObject(),
                CallbackID = request.CallbackID
            };

            if(!string.IsNullOrEmpty(request.ToUserId))
            {
                info.ToUserId = request.FromUserId;
                info.FromUserId = request.ToUserId;
            }

            return info;
        }

 

二、本地回调函数的处理及界面处理

调用方在收到服务器的应答消息的时候,会根据回调的ID ,从本地集合里面调出来并执行处理,实现了咱们回调的操做。线程

                var md5 = MD5Util.GetMD5_32(message.Content);
                if (md5 == message.MD5)
                {
                    InvokeMessageCallback(message, message.DeleteCallbackAfterInvoke);//触发回调

                    OnMessageReceived(message);//给子类重载
                }
        /// <summary>
        /// 执行回调函数
        /// </summary>
        /// <param name="msg">消息基础对象</param>
        /// <param name="deleteCallback">是否移除回调</param>
        private void InvokeMessageCallback(BaseMessage msg, bool deleteCallback)
        {
            var callBackObject = callBackList.SingleOrDefault(x => x.ID == msg.CallbackID);
            if (callBackObject != null)
            {
                if (deleteCallback)
                {
                    callBackList.Remove(callBackObject);
                }
                callBackObject.CallBack.DynamicInvoke(msg);
            }
        }

这样,咱们在调用的时候,传入一个回调的Action,让收到消息后进行动态执行就能够了。例如在登录的时候,咱们若是须要在登录成功后显示主窗体,那么能够执行下面的处理代码。设计

            var request = new AuthRequest(userNo, password);
            var message = request.PackData();
            Singleton<CommonManager>.Instance.Send(message, (msg) =>
            {
                var instance = Singleton<CommonManager>.Instance;
                try
                {
                    var response = JsonTools.DeserializeObject<AuthResponse>(msg.Content);
                    if (response.ValidateResult == 0)
                    {
                        instance.ShowLoadFormText("登陆成功,加载基础数据。。。。");

                        //放置初始化代码
                        Portal.gc.User = new User(userNo);
                        instance.SetClientId(userNo);

                        instance.ShowMainForm();
                        instance.CloseLoadForm();
                    }
                    else
                    {
                        instance.CloseLoadForm();
                        instance.ShowMessage("登陆失败:" + response.Message);
                        instance.ShowLogin();
                    }
                }
                catch (Exception ex)
                {
                    instance.ShowMessage("初始化异常:" + ex.Message);
                    instance.Exit();
                }
            });

或者咱们来看看另一个例子,这个例子是在用户登录的时候,请求一次在线用户列表,若是用户在线,那么在界面上展现列表,具体操做代码以下所示,也是利用了回调函数的处理方式。3d

        /// <summary>
        /// 发送获取在线用户列表的请求,并在收到应答数据后进行本地界面更新
        /// </summary>
        private void RefreshUser()
        {
            CommonRequest request = new CommonRequest(DataTypeKey.UserListRequest);
            var data = request.PackData();

            Singleton<CommonManager>.Instance.Send(data, (msg) =>
            {
                UserListResponse response = JsonTools.DeserializeObject<UserListResponse>(msg.Content);
                if (response != null)
                {
                    this.InvokeUI(() =>
                    {
                        this.listView1.Items.Clear();
                        foreach (CListItem item in response.UserList)
                        {
                            if (item.Value != Portal.gc.User.UserNo)
                            {
                                this.listView1.Items.Add(item.Text, 0);
                            }
                        }
                    });
                }
            });
        }

例如,客户端登录几个用户后,用户能够得到在线用户列表,界面展现以下所示。code

以上就是咱们在Socket应用里面处理回调函数的实现过程,这样处理能够很好利用回调代码来封装处理的细节,对于理解相关的应答操做也是很直观的。orm

相关文章
相关标签/搜索