在一般的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); 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给复制到应答的消息体里面,如下所示。

        /// <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;
}

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, 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 == )
{
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();
}
});

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

        /// <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, );
}
}
});
}
});
}

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

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

Socket开发框架之消息的回调处理的更多相关文章

  1. Socket开发框架之数据加密及完整性检查

    在前面两篇介绍了Socket框架的设计思路以及数据传输方面的内容,整个框架的设计指导原则就是易于使用及安全性较好,可以用来从客户端到服务端的数据安全传输,那么实现这个目标就需要设计好消息的传输和数据加 ...

  2. Socket开发框架之数据传输协议

    我在前面一篇随笔<Socket开发框架之框架设计及分析>中,介绍了整个Socket开发框架的总体思路,对各个层次的基类进行了一些总结和抽象,已达到重用.简化代码的目的.本篇继续分析其中重要 ...

  3. C#的Socket简单实现消息发送

    Socket一般用于网络之间的通信,在这里,实现的是服务端与客户端的简单消息通信.首先是客户端的搭建,一般步骤是先建立Socket绑定本地的IP和端口,并对远端连接进行连接进行监听,这里的监听一般开启 ...

  4. java版Web Socket,实现消息推送

    # web socket是什么? WebSocket协议是基于TCP的一种新的网络协议. 它实现了浏览器与服务器全双工(full-duplex)通信,允许服务器主动发送信息给客户端. ## 用途 实时 ...

  5. C# Socket异步实现消息发送--附带源码

    前言 看了一百遍,不如动手写一遍. Socket这块使用不是特别熟悉,之前实现是公司有对应源码改改能用. 但是不理解实现的过程和步骤,然后最近有时间自己写个demo实现看看,熟悉熟悉Socket. 网 ...

  6. ZeroMQ接口函数之 :zmq_msg_send – 从一个socket发送一个消息帧

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_msg_send zmq_msg_send(3) ØMQ Manual - ØMQ/3.2.5 Name zmq_ ...

  7. Java开发笔记(一百一十四)利用Socket传输文本消息

    前面介绍了HTTP协议的网络通信,包括接口调用.文件下载和文件上传,这些功能固然已经覆盖了常见的联网操作,可是HTTP协议拥有专门的通信规则,这些规则一方面有利于维持正常的数据交互,另一方面不可避免地 ...

  8. 电信NBIOT平台的CA证书上传-消息订阅回调地址检测503错误

    在NBIOT北向开发过程中,遇到消息订阅回调地址检测503错误,经过论坛查询与文档查阅一直都没有解决问题,大多人都说是RESTful地址格式问题,但其实不是.最终发现是我们在电信平台创建应用时,上传C ...

  9. 利用socket.io实现消息实时推送

    最近在写的项目中存在着社交模块,需要实现这样的一个功能:当发生了用户被点赞.评论.关注等操作时,需要由服务器向用户实时地推送一条消息.最终完成的项目地址为:socket-message-push,这里 ...

随机推荐

  1. 在.net中使用aquiles访问Cassandra(一)

    aquiles是.net下基于Thrift协议访问Cassandra的第三方类库,官方地址是: http://aquiles.codeplex.com/   1.下载类库文件: http://aqui ...

  2. [蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)

    开机初始化Log Log编号 函数名   所在文件名 000001: main ..\main.c 000002: timers_init ..\main.c 000003: gpiote_init ...

  3. AngularJS快速入门指南14:数据验证

    thead>tr>th, table.reference>tbody>tr>th, table.reference>tfoot>tr>th, table ...

  4. Java基础之常用类

    1.Collections类: (1)此类完全由在 collection 上进行操作或返回 collection 的静态方法组成. (2)静态方法摘要: static <T> boolea ...

  5. Java基础之I/O和file

    五.IO流1.IO流概述 (1)用来处理设备(硬盘,控制台,内存)间的数据. (2)java中对数据的操作都是通过流的方式. (3)java用于操作流的类都在io包中. (4)按照流操作的数据的类型不 ...

  6. redis中使用java脚本实现分布式锁

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/115.html?1455860390 edis被大量用在分布式的环境中,自 ...

  7. Bootstrap~日期控制

    回到目录 一个成熟的框架,日期控制是少不了的,在网上也有很多日期控制可以选择,而主框架用了bootstrap,日期控制也当前要用它自己的, 控件地址:http://www.bootcss.com/p/ ...

  8. lua跨平台文件夹遍历匹配查找

    require"lfs" --[[Desc:在B路径D文件中下 搜寻A路径下的没用到的C类文件: 并且将没用到的B类文件名称打印出来: 设置好路径拖到lua自带编辑器中即可运行之; ...

  9. phpstudy80端口被占用时的解决方案

    1.适合人群? 之前笔记本单独安装过Apache.php.mysql环境,但是后期想用集成开发环境phpstudy的,安装完phpstudy后(之前的单独环境依然存在),发现启动时,总是显示80端口被 ...

  10. 客户端向服务端传送特殊字符解决方法(检测到有潜在危险的 Request.Form 值)

    当客户端向服务端传输特殊字符时报错,错误信息如下图: