ESFramework介绍之(16)―― Tcp数据自动发送器ITcpAutoSender

简介: 我们已经积累了这样的经验:如果有一个大块的数据需要通过Tcp发送,我们会采用异步的方式以避免当前工作线程阻塞。那么,如果我们有多个线程需要同时发送大块的数据了?每个线程都在NetworkStream或Socket上提交异步发送数据的请求会导致数据发送的混乱(多个线程同时在一个Socket上进行写操作),所以,我引入了前面介绍的线程安全的网络流。

    我们已经积累了这样的经验:如果有一个大块的数据需要通过Tcp发送,我们会采用异步的方式以避免当前工作线程阻塞。那么,如果我们有多个线程需要同时发送大块的数据了?每个线程都在NetworkStream或Socket上提交异步发送数据的请求会导致数据发送的混乱(多个线程同时在一个Socket上进行写操作),所以,我引入了前面介绍的线程安全的网络流。在引入这个类后,似乎日子已经很好过了,但是新的功能要求使得我需要寻找另外的解决方案。
    考虑一下这个情景:我们的即时通信软件AgileIM在和好友进行视频聊天的同时,还要传递文字信息、音频数据、重要文件数据、控制命令等。而需要被传送的这些信息是有优先级顺序的,比如,控制命令的优先级最高,文件数据的优先级较低,而视频数据/音频数据在网络特别繁忙的时候是可以丢弃的。为了管理这些优先级,仅仅依靠ISafeNetworkStream已经不能满足我们的要求,我们需要另外一个组件来为我们解决这个问题,ESFramework提供的ITcpAutoSender组件正是为此目的的。

    我们知道,在多线程的环境中对像Socket这样临界资源的访问必须保证线程安全,从另外一个角度来看,可以这么说,对某个临界资源的访问必须是同步的或者说必须是“仿单线程”的。 ITcpAutoSender就利用了这一“仿单线程”特性使的问题简单化。我们来看看这个组件究竟是如何工作的?
    我们的应用程序中的各个线程当有数据需要发送时,就将数据提交ITcpAutoSender,提交时必须指定该数据的优先级。ITcpAutoSender组件会根据指定的优先级将该数据放入到对应的队列中,请注意,将要发送的数据提交给ITcpAutoSender必须是线程安全的,这点已经由ITcpAutoSender组件保证,使用者不用关心。

    ITcpAutoSender内部有一个循环线程,每次从高优先级的队列中选取一个数据包进行发送,当高优先级队列为空时,再去发送次高优先级队列中的数据。每当一个数据包发送完毕,就再从高优先级的队列开始检查,如此反复。
    从上面的描述已经可以看出,ITcpAutoSender组件仅仅是在一个线程中发送数据,所以ITcpAutoSender组件不需要再借助ISafeNetworkStream组件,而是直接使用NetworkStream就可以了。那么是不是ESFramework就不需要提供ISafeNetworkStream组件了?不是。在ESFramework框架中,ISafeNetworkStream组件主要用于服务端,因为通常情况下,服务端主动发送数据给客户端的几率比较小,而且服务端要管理成千上万的连接,所以使用ISafeNetworkStream组件是非常合适的。而ITcpAutoSender组件最常用于客户端,为客户端应用提供发送的数据的优先级机制。如果对你的应用中的客户端来说,所有的数据的优先级是一样的,那么就没有必要使用ITcpAutoSender组件了,直接使用ISafeNetworkStream就可以了。
 
    在ESFramework框架中,将数据的优先级分为4等,如下枚举定义所示:

1     public   enum  DataPriority
2      {
3          High , // 紧急命令
4          Common , // 如普通消息,如聊天消息
5          Low , // 如文件传输
6          CanBeDiscarded  // 如视频数据、音频数据
7      }

    对于每个优先级,ITcpAutoSender组件的实现中都有一个对应的队列,每个队列都设置了初始大小。对于前三个优先级队列,当它们Full的时候,应用中的线程再提交数据就必须阻塞等待。而对于最低优先级CanBeDiscarded的队列,如果Full的时候有数据提交过来,则会删除队列头部的待发送数据。下面给出ITcpAutoSender组件的接口定义:
   

 1      public   interface  ITcpAutoSender :IDisposable
 2      {
 3           void  Initialize() ;
 4           void  SendData( byte [] data, DataPriority dataPriority);      
 5           void  ClearQueue(DataPriority queueType) ;
 6           event  CbDataDiscarded DataDiscarded;
 7           event  CbDataLacked    DataLacked;
 8           event  CbSimple        ConnectionInterrupted ;
 9 
10           int  QueueSizeOfDiscarded {  get set ;}
11           int  QueueSizeOfNonDiscarded {  get set ;}
12          NetworkStream NetworkStream {  set ;}
13      }    
14 
15       public   delegate   void  CbDataDiscarded( byte [] data) ;
16       public   delegate   void  CbDataLacked() ;

    注意,这个接口中发布DataDiscarded事件和DataLacked事件,当网络繁忙有数据被抛弃时,DataDiscarded事件被触发;当所有的待发送队列都为空时,DataLacked事件被触发。我们的应用可以预定这两个事件来作些适当的调度。比如在AgileIM中,当DataDiscarded事件发生时,就适当减少视频捕获的帧率;当DataLacked事件发生时,就适当增大视频捕获的帧率,以达到更好的视频会话效果。

    最后,说一下,很多朋友想得到ESFramework的源代码,这要等到ESFramework开源以后,相信这不是很久远的事情了。这里,我把ESFramework.dll提供给大家下载适用,当然你也可以用反射工具看看其中的构造。如果你想在你的项目中使用ESFramework,我将非常乐意回答你遇到的各种问题,你可以通过AgileSoft@163.com联系我。
    谢谢关注!

上一篇文章:ESFramework介绍之(15)-- IRAS

转到  :ESFramework 可复用的通信框架(序) 

 

目录
相关文章
|
网络协议 物联网 开发者
NB-IoT 通信之 TCP 收发数据 | 学习笔记
快速学习 NB-IoT 通信之 TCP 收发数据
801 0
NB-IoT 通信之 TCP 收发数据 | 学习笔记
|
传感器 数据采集 JSON
RS232/RS485转4G DTU 上传基于Modbus协议的温湿度传感器数据到远程TCP服务器
RS232/RS485转4G DTU 上传基于Modbus协议的温湿度传感器数据到远程TCP服务器
559 0
RS232/RS485转4G DTU 上传基于Modbus协议的温湿度传感器数据到远程TCP服务器
|
2月前
|
网络协议 开发者 Python
tcp/ip模型中,帧是第几层的数据单元?
在网络通信的世界中,TCP/IP模型以其高效和可靠性而著称。这个模型是现代互联网通信的基石,它定义了数据在网络中如何被传输和接收。其中,一个核心的概念是数据单元的层级,特别是“帧”在这个模型中的位置。今天,我们就来说一下TCP/IP模型中帧的概念,以及它作为数据单元在哪一层中扮演着关键角色。
|
存储 监控 网络协议
搞了半天,终于弄懂了TCP Socket数据的接收和发送,太难
本文将从上层介绍Linux上的TCP/IP栈是如何工作的,特别是socket系统调用和内核数据结构的交互、内核和实际网络的交互。写这篇文章的部分原因是解释监听队列溢出(listen queue overflow)是如何工作的,因为它与我工作中一直在研究的一个问题相关。 建好的连接怎么工作 先从建好的连接开始介绍,稍后将解释新建连接是如何工作的。
517 0
搞了半天,终于弄懂了TCP Socket数据的接收和发送,太难
|
JSON 网络协议 Android开发
【Android 逆向】Android 逆向通用工具开发 ( Android 端远程命令工具 | Android 端可执行程序的 main 函数操作 | TCP 协议服务器建立 | 接收客户端数据 )
【Android 逆向】Android 逆向通用工具开发 ( Android 端远程命令工具 | Android 端可执行程序的 main 函数操作 | TCP 协议服务器建立 | 接收客户端数据 )
181 0
【Android 逆向】Android 逆向通用工具开发 ( Android 端远程命令工具 | Android 端可执行程序的 main 函数操作 | TCP 协议服务器建立 | 接收客户端数据 )
|
网络协议 Java
【Java 网络编程】TCP 传输机制 ( 数据拆分 | 排序 | 顺序发送 | 顺序组装 | 超时重发 )
【Java 网络编程】TCP 传输机制 ( 数据拆分 | 排序 | 顺序发送 | 顺序组装 | 超时重发 )
226 0
【Java 网络编程】TCP 传输机制 ( 数据拆分 | 排序 | 顺序发送 | 顺序组装 | 超时重发 )
|
网络协议
如何向通过TCP socket给服务器端发送的数据里注明自己的客户端地址
服务器端监听在端口8081后,客户端发起连接,客户端的port为54436.
如何向通过TCP socket给服务器端发送的数据里注明自己的客户端地址
|
网络协议
如何向通过TCP socket给服务器端发送的数据里注明自己的客户端地址
服务器端监听在端口8081后,客户端发起连接,客户端的port为54436.
如何向通过TCP socket给服务器端发送的数据里注明自己的客户端地址
|
网络协议
tcp回显客户端发送的数据
客户端: import socket tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcp_socket.connect(('127.
938 0