如何使用网络库实现应用级消息收发

网络客户端ISocketClient和网络会话ISocketSession都继承了ISocketRemoteISocketRemote表示远程通信,核心就是收发数据。
下面是ISocketRemote接口的主要实现

/// <summary>远程通信Socket,仅具有收发功能</summary>
public interface ISocketRemote : ISocket
{
    #region 属性
    /// <summary>远程地址</summary>
    NetUri Remote { get; set; }

    /// <summary>通信开始时间</summary>
    DateTime StartTime { get; }

    /// <summary>最后一次通信时间,主要表示会话活跃时间,包括收发</summary>
    DateTime LastTime { get; }

    /// <summary>缓冲区大小</summary>
    Int32 BufferSize { get; set; }
    #endregion

    #region 发送
    /// <summary>发送数据</summary>
    /// <remarks>
    /// 目标地址由<seealso cref="Remote"/>决定
    /// </remarks>
    /// <param name="pk">数据包</param>
    /// <returns>是否成功</returns>
    Boolean Send(Packet pk);
    #endregion

    #region 接收
    /// <summary>接收数据。阻塞当前线程等待返回</summary>
    /// <returns></returns>
    Packet Receive();

    /// <summary>数据到达事件</summary>
    event EventHandler<ReceivedEventArgs> Received;

    /// <summary>消息到达事件</summary>
    event EventHandler<MessageEventArgs> MessageReceived;
    #endregion

    #region 数据包处理
    /// <summary>粘包处理接口</summary>
    IPacket Packet { get; set; }

    /// <summary>异步发送数据并等待响应</summary>
    /// <param name="pk"></param>
    /// <returns></returns>
    Task<Packet> SendAsync(Packet pk);

    /// <summary>发送消息并等待响应</summary>
    /// <param name="msg"></param>
    /// <returns></returns>
    Task<IMessage> 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.

时间: 2024-10-13 17:17:48

如何使用网络库实现应用级消息收发的相关文章

boost.ASIO-可能是下一代C++标准的网络库

曾几何时,Boost中有一个Socket库,但后来没有了下文,C++社区一直在翘首盼望一个标准网络库的出现,网络上开源的网络库也有不少,例如Apache Portable Runtime就是比较著名的一个,也有像ACE这样重量级的网络框架.去年,Boost将ASIO纳入了自己的体系,由于Boost的影响力,ASIO有机会成为标准网络库.作者Chris Kohlhoff以ASIO为样本向C++标准委员会提交了一个网络库建议书,里面提到:ASIO的覆盖范围: Networking using TC

0806------Linux网络编程----------Echo 网络库 学习笔记

1.Echo网络库的编写 1.1 Echo网络库1.0 1.1.1 Echo网络库 1.0 框架分析 a)class InetAddress: 主要用来定义一个struct sockaddr_in 结构(用自定义端口号初始化),并提供获取这个结构体成员如IP.Port等的接口: b)class Socket : 主要用来把一个普通的 sockfd 变为 listenfd(这里用一个sockfd初始化对象),提供bind .listen.accept 等接口. c)class TcpConnect

Muduo网络库源码分析(一) EventLoop事件循环(Poller和Channel)

从这一篇博文起,我们开始剖析Muduo网络库的源码,主要结合<Linux多线程服务端编程>和网上的一些学习资料! (一)TCP网络编程的本质:三个半事件 1. 连接的建立,包括服务端接受(accept) 新连接和客户端成功发起(connect) 连接.TCP 连接一旦建立,客户端和服务端是平等的,可以各自收发数据. 2. 连接的断开,包括主动断开(close 或shutdown) 和被动断开(read(2) 返回0). 3. 消息到达,文件描述符可读.这是最为重要的一个事件,对它的处理方式决定

Cowboy.WebSockets 开源 WebSocket 网络库

Cowboy.WebSockets 是一个托管在 GitHub 上的基于 .NET/C# 实现的开源 WebSocket 网络库,其完整的实现了 RFC 6455 (The WebSocket Protocol) 协议标准,并部分实现了 RFC 7692 (Compression Extensions for WebSocket) 协议标准. WebSocket 可理解为建立在 TCP 连接通道上的更进一步的握手,并确定了消息封装格式. 通过定义控制帧 (Control Frame) 和数据帧

Linux多线程服务端编程:使用muduo C++网络库

内容推荐本 书主要讲述采用现代C++在x86-64 Linux上编写多线程TCP网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread.这是在Linux下以native语言编写用户态高性能网络程序最成熟的模式,掌握之后可顺利地开发各类常见的服务端网络应用程序.本书以 muduo网络库为例,讲解这种编程模型的使用方法及注意事项.本书的宗旨是贵精不贵多.掌握两种基本的同步原语就可以满足各种多线程同步的功能需求,还能写出更易用的同步设施.掌

基于c++11新标准开发一个支持多线程高并发的网络库

背景 新的c++11标准出后,c++语法得到了很多的扩展,比起以往任何时候都要灵活和高效,提高了程序编码的效率,为软件开发人员节省了不少的时间. 之前我也写过基于ACE的网络服务器框架,但ACE毕竟有些臃肿,内部对象关系错综复杂,容易给人造成只见树木不见森林的错觉. 所以打算用c++11开发一个较为简洁,高效,支持高并发的网络库. 开源         花了两三周,终于把基础的结构开发完成,代码也开源在github上,网址是 https://github.com/lichuan/fly 欢迎各位

Android实战之你应该使用哪个网络库?

前言 目前基本上每个应用都会使用HTTP/HTTPS协议来作为主要的传输协议来传输数据.即使你没有直接使用HTTP协议,也会有成堆的SDK会包含这些协议,譬如分析.Crash反馈等等.当然,目前也有很多优秀的HTTP的协议库,可以很方便的帮助开发者构建应用,本篇博文中会尽可能地涵盖这些要点.Android的开发者在选择一个合适的HTTP库时需要考虑很多的要点,譬如在使用Apache Client或者HttpURLConnection时可能会考虑: 能够取消现有的网络请求 能够并发请求 连接池能够

iOS常用网络库收集

一 ASIHttpRequest二 AFNetworking 三 AFDownloadRequestOperationA progressive download operation for AFNetworking. 四 MKNetworkKit Github地址:https://github.com/MugunthKumar/MKNetworkKit介绍文章:http://www.cnblogs.com/scorpiozj/p/3222803.html License:MIT License

客户端网络库实现真的很简单吗?

(注:本文所讲的网络协议只针对TCP协议) 背景:开发一个C/S的应用势必需要服务端和客户端的适配,包括网络协议.数据传输格式.业务处理的适配.由于服务端承载着大量的客户端,需要高并发.高性能.高可靠性,在我们的认知里往往认为服务端的网络模型和架构设计很复杂.但是客户端嘛,无非就是建立网络连接,发个请求收个回复如此简单.所以在工作中经常会出现有些客户端处理界面和业务的同事对平台开发者说,你做好服务端的网络就好,客户端的网络我来处理,而且在他们的想法里,这个所谓的客户端网络库只需要很短的时间就可以