在一般的Socket应用里面,很多时候数据的发送和接收是分开处理的,也就是我们发送一个消息,不知道这个请求消息什么时候得到应答消息,而且收到对应的应答消息的时候,如果操作界面的内容,也是需要特别处理的,因为它们和界面线程是不在一起的。如果我们在发送消息的时候,能够给一段回调的代码给收到应答消息的时候处理,那么就会方便很多。本文主要介绍如何在Socket应用里面,通过回调函数的处理,实现收到应答消息的时候能够调用对应的函数。

1、回调函数的设计

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

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

但我们需要发送消息的时候,我们把回调的ID添加到本地集合里面,得到应答的时候,在从集合里面提出来,执行就可以了。

        /// <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);returnresult;
}
        /// <summary>
        ///记录回调的函数信息/// </summary>
        /// <param name="callBack"></param>
        /// <param name="msg"></param>
        private voidAddCallback(Delegate callBack, BaseMessage msg)
{
if (callBack != null)
{
Guid callbackID
=Guid.NewGuid();
ResponseCallbackObject responseCallback
= newResponseCallbackObject()
{
ID
=callbackID,
CallBack
=callBack
};

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

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

        /// <summary>
        ///封装数据进行发送(复制请求部分数据)/// </summary>
        /// <returns></returns>
        publicBaseMessage PackData(BaseMessage request)
{
BaseMessage info
= newBaseMessage()
{
MsgType
= this.MsgType,
Content
= this.SerializeObject(),
CallbackID
=request.CallbackID
};
if(!string.IsNullOrEmpty(request.ToUserId))
{
info.ToUserId
=request.FromUserId;
info.FromUserId
=request.ToUserId;
}
returninfo;
}

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

调用方在收到服务器的应答消息的时候,会根据回调的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, booldeleteCallback)
{
var callBackObject = callBackList.SingleOrDefault(x => x.ID ==msg.CallbackID);if (callBackObject != null)
{
if(deleteCallback)
{
callBackList.Remove(callBackObject);
}
callBackObject.CallBack.DynamicInvoke(msg);
}
}

这样,我们在调用的时候,传入一个回调的Action,让收到消息后进行动态执行就可以了。例如在登陆的时候,我们如果需要在登陆成功后显示主窗体,那么可以执行下面的处理代码。

            var request = newAuthRequest(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 = newUser(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();
}
});

或者我们来看看另外一个例子,这个例子是在用户登陆的时候,请求一次在线用户列表,如果用户在线,那么在界面上展示列表,具体操作代码如下所示,也是利用了回调函数的处理方式。

        /// <summary>
        ///发送获取在线用户列表的请求,并在收到应答数据后进行本地界面更新/// </summary>
        private voidRefreshUser()
{
CommonRequest request
= newCommonRequest(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 inresponse.UserList)
{
if (item.Value !=Portal.gc.User.UserNo)
{
this.listView1.Items.Add(item.Text, 0);
}
}
});
}
});
}

例如,客户端登陆几个用户后,用户可以获得在线用户列表,界面展示如下所示。

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

标签: none

添加新评论