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

相關文章
相關標籤/搜索