博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何使用网络库实现应用级消息收发
阅读量:6263 次
发布时间:2019-06-22

本文共 3042 字,大约阅读时间需要 10 分钟。

网络客户端ISocketClient和网络会话ISocketSession都继承了ISocketRemoteISocketRemote表示远程通信,核心就是收发数据。

下面是ISocketRemote接口的主要实现

复制代码
/// 远程通信Socket,仅具有收发功能public interface ISocketRemote : ISocket{    #region 属性    /// 远程地址    NetUri Remote { get; set; }    /// 通信开始时间    DateTime StartTime { get; }    /// 最后一次通信时间,主要表示会话活跃时间,包括收发    DateTime LastTime { get; }    /// 缓冲区大小    Int32 BufferSize { get; set; }    #endregion    #region 发送    /// 发送数据    /// 
/// 目标地址由
决定 ///
/// 数据包 ///
是否成功
Boolean Send(Packet pk); #endregion #region 接收 /// 接收数据。阻塞当前线程等待返回 ///
Packet Receive(); /// 数据到达事件 event EventHandler
Received; ///
消息到达事件 event EventHandler
MessageReceived; #endregion #region 数据包处理 ///
粘包处理接口 IPacket Packet { get; set; } ///
异步发送数据并等待响应 ///
///
Task
SendAsync(Packet pk); ///
发送消息并等待响应 ///
///
Task
SendAsync(IMessage msg); #endregion}
复制代码

 

一、同步收发
一般小型网络应用,或者个人学习程序,都会使用同步收发。
Send(xxx);
var buf = Receive();
这样向对服务端发一个数据包,然后同步阻塞等待接收一个响应数据。
同步收发最大的优点就是简单,容易理解;
最大的缺点是性能极其底下,并且很大的几率会失败抛出异常,特别是离开本机或者局域网以后。
除非网络很干净,客户端服务端只进行很简单的通信,否则出错崩溃就是家常便饭!
并且,这个阶段的工程师,一般认为只能客户端向服务端发数据,而不知道服务端可以主动向客户端发数据。
因此,15年经验表明,同步收发根本不适合做产品级应用!
二、事件驱动
中大型网络应用,一般采用事件驱动,特别是多并发服务端。
不管是APM还是SAEA,绝大多数网络框架都会包装成为事件,或者路由分发架构。
正如前文接口图黄色箭头所示,事件驱动一般用法:
client.Received += OnReceive;
client.Send(xxx);
先建立接收事件,然后发送数据,如果对方有响应,就会触发OnReceive函数,对响应结果进行处理。
事件驱动(包括路由分发)是当下网络框架主流,占比超过70%
几乎所有框架都会在此之外再包装一层,Send一个业务对象,内部序列化为数据后发出,OnReceive后反序列化得到业务对象,返回给上层。
事件驱动跟同步业务需求是相背而行的。
如果业务需要向服务端发送一个请求,然后等待响应结果,那么事件驱动甚至还不如同步操作好用!
一般做法是Send里面做堵塞等待,然后OnReceive里面做拦截。
这也是事件驱动无法进一步扩大比例的根本原因。
事件驱动很好很强大,只是特别不适应业务上的同步操作需求!
三、异步请求响应
近20年的软件发展史,无一例外等同于Web发展史。
除了技术的发展,Web思维影响了几乎所有软件工程师。哪怕初学者,也很清楚HTTP是请求响应模型。在Web开发里面,所有的业务都要基于请求与响应。
于是我们网络库有了第三种选择。(前文接口图紫色箭头)
Task<Packet> SendAsync(Packet pk);
Task<IMessage> SendAsync(IMessage msg);
event EventHandler<MessageEventArgs> MessageReceived;
异步发送SendAsync,可以像事件模型那样在MessageReceived里面处理,也可以 var rs = await SendAsync(pk); 把异步转为同步操作,满足同步业务需求。
更为重要的是,SendAsync支持单连接通道并行多异步请求
也就是说,在一个网络连接上,第一个请求的响应还没有收到之前,业务逻辑可以连续发出更多的请求,不管这些请求的响应包先后顺序以后,网络库都能够准确配对,让await SendAsync得到正确的结果。
这就解决了一个极为常见的问题,一个业务应用里面,可能多个线程需要向服务端请求数据,而传统做法只能是加锁,在第一个请求响应完成之前,阻塞其它请求。
实际上,HTTP 1.0/1.1正是传统做法,前一个请求完成之前,不能发起新的请求,导致浏览器不得不建立多个Tcp连接。
因此,异步请求响应的架构设计,让请求响应准确配对,支持并行请求,并且解决一切粘包问题!
应用级消息收发伪代码:

var str = "{action:Open,args:{index:3},remark:打开3号灯}";var client = new NetUri("tcp://127.0.0.1:1234").CreateRemote();client.Packet = new DefaultPacket();var rs = await client.SendAsync(str.GetBytes());// rs = "{result:true,data:3号灯已打开}"

上面的DefaultPacket正是 

请求响应包的头部,都会增加4字节,Json字符串作为负载数据。
正是增加的这4字节,确保了请求响应的准确配对(序列号匹配),解决了粘包问题(头部长度)
即使没有默认封包DefualtPacket,上面代码也是可以工作的,只是这样就失去了准确配对和粘包拆分,要求业务层不能频繁收发。
End.

我不相信神话,我只相信汗水!我不相信命运,我只相信双手!
分类: ,
本文转自大石头博客园博客,原文链接:http://www.cnblogs.com/nnhy/p/7455792.html,如需转载请自行联系原作者
你可能感兴趣的文章
100个推荐的图片/内容滑动条
查看>>
秋式广告杀手:广告拦截原理与杀手组织
查看>>
内存溢出
查看>>
如何重启IIS进程
查看>>
分享一个javascript alert精简框架
查看>>
【解决方法】System.IO.FileNotFoundException
查看>>
Android 命令行编译、打包生成apk文件
查看>>
java中解决组件重叠的问题(例如鼠标移动组件时)
查看>>
使用 Navicat 8.0 管理mysql数据库(导出导入数据)
查看>>
视频会议
查看>>
EntityFramework系列:SQLite.CodeFirst自动生成数据库
查看>>
网络编码
查看>>
定时任务-在spring中配置quartz
查看>>
【springMVC 后台跳转前台】1.使用ajax访问的后台,后台正常执行,返回数据,但是不能进入前台的ajax回调函数中 ----2.前后台都没有报错,不能进入ajax回调函数...
查看>>
redis+Keepalived主从热备秒级切换
查看>>
Hibernate占位符警告:use named parameters or JPA-style positional parameters instead.
查看>>
基于 IdentityServer3 实现 OAuth 2.0 授权服务数据持久化
查看>>
是什么时候开始学习gulp了
查看>>
【Cocos2d-x游戏开发】细数Cocos2d-x开发中那些常用的C++11知识
查看>>
otl使用存储过程或是LEFT JOIN时提示输出类型未知的问题
查看>>