zoukankan      html  css  js  c++  java
  • ESFramework介绍之(16)―― Tcp数据自动发送器ITcpAutoSender

        我们已经积累了这样的经验:如果有一个大块的数据需要通过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 { getset;}
    11         int QueueSizeOfNonDiscarded { getset;}
    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 可复用的通信框架(序) 

     

  • 相关阅读:
    第四篇Scrum冲刺博客
    第三篇Scrum冲刺博客
    蔡勒公式和吉姆拉尔森公式计算星期几
    事后诸葛亮
    Alpha阶段项目复审
    团队作业6——复审与事后分析
    第7篇 Scrum 冲刺博客
    第6篇 Scrum 冲刺博客
    第5篇 Scrum 冲刺博客
    第4篇 Scrum 冲刺博客
  • 原文地址:https://www.cnblogs.com/zhuweisky/p/357852.html
Copyright © 2011-2022 走看看