最新版本的ESFramework/ESPlus提供了基于TCP和UDP的P2P通道,而无论我们是使用基于TCP的P2P通道,还是使用基于UDP的P2P通道,ESPlus保证所有的P2P通信都是可靠的。这是因为ESPlus在原始UDP的基础上模拟TCP的机制进行了再次封装,以使UDP像TCP一样可靠。在客户端之间需要高频通信的分布式系统中(如IM系统等),可靠的P2P通信将为您节省巨大的带宽和服务器成本。详情请参见:ESFramework 开发手册(04) -- 可靠的P2P 以及 ESFramework 使用技巧 -- 部署P2P服务器 

ESFramework 4.0 进阶(07)-- 消息同步调用一文中我们介绍了客户端与服务器进行交互的一种常见情况:客户端向服务器发送请求消息,服务器处理完毕后返回应答消息给客户端。还有一种常见情况是,客户端需要发送一个消息给另外一个在线的用户。一般,这样的P2P消息是通过服务器中转的。很多情况下,中转不会有很大的问题,但是对于那种类似用户之间需要视频会话、文件传输等高频率、大尺寸消息交互的应用来说,所有的消息都经过服务器中转,就会大大地增加服务器的压力。对于这种需求,使用P2P通道是最常见的解决方案。即,用户与用户之间交互的消息通过P2P通道来发送。

一.P2P通道管理器IP2PChannelManager

  ESFramework使用ESFramework.Passive.IP2PChannelManager来管理所有的P2P通道,IP2PChannelManager接口定义如下:


    public interface IP2PChannelManager
    {    
        /// <summary>
        /// 通向目标用户的P2P通道是否可用。
        /// </summary>
        /// <param name="destUserID">目标用户ID</param>        
        bool P2PChannelUsable(string destUserID) ;         /// <summary>
        /// 通过P2PChannel发送消息。
        /// </summary>
        /// <param name="destUserID">目标用户ID</param>
        /// <param name="msg">要发送的消息</param>
        /// <param name="dataPriority">发送的优先级</param>
        void SendMessage(string destUserID, IMessage msg, DataPriority dataPriority);
    }   

如果应用中,不需要使用P2P通道,则可以直接使用null object模式的ESFramework.Passive.EmptyP2PChannelManager类。

实现P2P通道的最常见方式就是P2P打洞,而ESPlus提供了现成基于TCP的P2P通道管理器ESPlus.Application.P2PSession.Passive.Tcp.TcpChannelManager和基于UDP的P2P通道管理器ESPlus.Application.P2PSession.Passive.Udp.UdpChannelManager供我们使用。但是,用起来并不简单,要想成功的部署支持P2P的应用,还需要部署NAPT服务器和P2P服务器,以支持P2P打洞和P2P通道的建立。这些已经不是ESFramework的核心内容,这里就不展开了。

如果使用者能实现自己的P2P通道(比如基于UPnP),那么只要实现IP2PChannelManager接口,并挂接到MessageTransceiver就可以了。

二. 消息收发器IMessageTransceiver

ESFramework通过ESFramework.Passive.IMessageTransceiver来支持P2P通道的挂接,IMessageTransceiver的主要作用是向使用者屏蔽了P2P消息是经过服务器中转的还是经由P2P通道发送的,这使得底层的通道选择对于上层应用开发者是透明的。

IMessageTransceiver接口定义如下:


    public interface IMessageTransceiver
    {
        IP2PChannelManager P2PChannelManager { set; }
        IServerAgent ServerAgent { set; }
        IContractHelper ContractHelper { set; }
        IResponseManager ResponseManager { set; }   
        IMessagePipe MessagePipe { set; }         /// <summary>
        /// 提交数据。
        /// (1)如果为非P2P消息,则直接向服务器提交。
        /// (2)如果消息为P2P(即接收者是另一个在线用户),且目标用户的P2PChannel可用,则将通过P2PChannel向该用户发送。否则,也是直接向服务器提交。     
        /// </summary>
        /// <param name="msg">要提交的消息</param>
        /// <param name="dataPriority">消息发送的优先级</param>
        void CommitRequest(IMessage msg, DataPriority dataPriority);         /// <summary>
        /// 提交数据并返回应答。如果resMessageType不为null,且超时仍然没有回复,则抛出超时异常。
        /// (1)如果dataPriority != DataPriority.CanBeDiscarded ,则resMessageType只能为null。  
        /// (2)如果为非P2P消息,则直接向服务器提交。
        /// (3)如果消息为P2P(即接收者是另一个在线用户),且目标用户的P2PChannel可用,则将通过P2PChannel向该用户发送。否则,也是直接向服务器提交。     
        /// </summary>
        /// <param name="msg">要提交的消息</param>
        /// <param name="dataPriority">消息发送的优先级</param>
        /// <param name="resMessageType">期望应答消息的类型</param>
        /// <returns>应答消息</returns>     
        IMessage CommitRequest(IMessage msg, DataPriority dataPriority, int? resMessageType);         /// <summary>
        /// 向服务器提交数据。该消息即使消息为P2P,且P2P通道可用,也一定要经过服务器中转。
        /// </summary>
        /// <param name="requestMsg">要提交的消息</param>
        /// <param name="dataPriority">消息发送的优先级</param>
        /// <param name="resMessageType">期望应答消息的类型</param>
        /// <returns>应答消息</returns>
        IMessage CommitRequestToServer(IMessage requestMsg, DataPriority dataPriority, int? resMessageType);            
    }

(1)当消息的接收者(IMessage.Header.DestUserID)为其它在线用户时,MessageTransceiver会先查看是否存在可用的P2P通道,如果存在,则直接交由IP2PChannelManager发送;否则,还是通过IServerAgent发送给服务器中转。

(2)如果消息的接收者为服务器,则通过IServerAgent直接向服务器提交。

(3)如果消息为请求消息且需要应答,则使用第二个CommitRequest方法。注意,这个请求消息可能是被服务器处理,也可能是被另外一个在线客户端处理,这取决于消息接收者(IMessage.Header.DestUserID)是服务器还是另外一个在线用户。

(4)对于有些类型的P2P消息,我们的项目可能需要该消息必须经过服务器中转,即使对应的P2P通道存在,也一定要中转(比如,消息要被服务端记录),那么这个时候就可以直接调用CommitRequestToServer方法。

(5)IMessageTransceiver之所以要依赖于IMessagePipe,是因为要保证消息在使用P2P通道发送之前,也必须先经过消息骨架流程。

(6)IMessageTransceiver为何要依赖于回复管理器IResponseManager了,这是因为当请求消息经过P2P通道发送时,是不需要调用IServerAgent,所以MessageTransceiver需要自己从回复管理器中提取匹配的回复消息。

站在客户端的角度,我们看到从IRegularSender到IServerAgent、再到IMessageTransceiver,是一个逐步增强的过程,并且后一个组件的实现都是建立在前一个组件之上的。IRegularSender解决了发送的消息必须经过MessagePipe,IServerAgent对消息同步调用提供支持,而IMessageTransceiver又向使用者屏蔽了底层消息发送的通道。

还有一点要特别指出的是,P2P通道的接收端可以直接将接收到的消息交给MessageDispatcher去分派处理,这样就复用了我们的消息处理骨架流程。

三.示范代码

在客户端的编程开发中,发送消息时我们最好是使用IMessageTransceiver而不是IRegularSender或IServerAgent,即使暂时用不到P2P通道也没关系,以后如果有启动P2P通道的需求,那么代码就不用修改,只要在配置文件中使用正式的P2P通道管理器对象来替换EmptyP2PChannelManager即可。

最后,我们来构造一个IMessageTransceiver实例:


    IContractHelper contractHelper = ......;
    IResponseManager responseManager = ......;
    IServerAgent serverAgent = ......;
    IMessageTransceiver messageTransceiver = new MessageTransceiver();
    messageTransceiver.ContractHelper = contractHelper;
    messageTransceiver.P2PChannelManager = new EmptyP2PChannelManager();
    messageTransceiver.ResponseManager = responseManager;
    messageTransceiver.ServerAgent = serverAgent;     //IMessage response = messageTransceiver.CommitRequest(requestMessage, DataPriority.Common ,102);

挂接P2P通道-- ESFramework 4.0 进阶(08)的更多相关文章

  1. 垂直分割群集模型与多通道引擎 -- ESFramework 4.0 进阶(10)

    在ESFramework 4.0 进阶(09)-- ESPlatform 支持的三种群集模型一文中,我们介绍了ESPlatform支持的三种群集模型 -- 垂直分割模型.水平分割模型.交叉模型.我们看 ...

  2. 消息同步调用-- ESFramework 4.0 进阶(07)

    分布式系统的构建一般有两种模式,一是基于消息(如Tcp,http等),一是基于方法调用(如RPC.WebService.Remoting).深入想一想,它们其实是一回事.如果你了解过.NET的Prox ...

  3. 正规消息发送器-- ESFramework 4.0 进阶(06)

    在ESFramework 4.0 进阶(04)-- 驱动力:通信引擎(下)一文末尾我们已经将通信引擎以及整个消息骨架流程组装起来了,只要通信引擎一接收到消息,框架就会按照规定的流程进行运转.到这里,自 ...

  4. 驱动力—— 通信引擎(上)—— ESFramework 4.0 进阶(03)

    在ESFramework 4.0 进阶(02)-- 核心:消息处理的骨架流程一文中我们详细介绍了ESFramework中消息处理的骨架流程,并且我们已经知道,ESFramework中的所有通信引擎使用 ...

  5. ESFramework 4.0 进阶(04)-- 驱动力:通信引擎(下)

    在ESFramework 4.0 进阶(03)-- 驱动力:通信引擎(上)一文中,我们对ESFramework提供的每一个通信引擎的接口都做了详细了说明,这篇文章我们将继续探讨这些接口的实现类 -- ...

  6. 核心梳理——消息处理的骨架流程——ESFramework 4.0 进阶(02)

    在ESFramework 4.0 概述一文中,我们提到ESFramework.dll作为通信框架的核心,定义了消息处理的骨架流程,本文我们来详细剖析这个流程以及该骨架中所涉及的各个组件.ESFrame ...

  7. 好友与组--ESFramework 4.0 进阶(11)

    大部分分布式通信系统中,都会涉及到客户端之间相互通信.以及需要将客户端进行分组的功能,或者是类似这方面的需求.ESFramework对这一常见的任务内置了强大的支持,包括从客户端到服务端.一直到Pla ...

  8. 在线用户管理--ESFramework 4.0 进阶(05)

    无论我们采用何种通信框架来构建我们的分布式系统,在服务端进行用户管理都是非常重要的一个环节.然而用户管理是否应该隶属于通信框架了?这个并不一定,通常来说,用户管理是与具体应用紧密相关的,应该是由应用解 ...

  9. ESFramework 4.0 进阶(01)-- 消息

    需要交互的分布式系统之间通过消息来传递有意义的信息.消息是通信框架的核心.离开了消息,再谈通信框架就没有任何意义,所以,消息是ESFramework中一个最核心的概念. 一. 消息的类别 在具体的应用 ...

随机推荐

  1. 一步步优化JVM五:优化延迟或者响应时间

    本节的目标是做一些优化以满足对应用对延迟的需求.这次需要几个步骤,包括完善Java堆大小的配置,评估垃圾回收占用的时间和频率,也许还要尝试切换到不同的垃圾回收器,以及由于使用了不同的垃圾回收器,需要重 ...

  2. 相对协议-关于src里//开头的知识

    "相对协议",也就是链接以 // 开头,前面去掉了 http: 或 https: 字样, 这样做的好处是浏览器能够根据你的网站所采用的协议来自动加载 CDN 上托管的文件!

  3. form -转载于blfshiye

    Form API 表单API 文件夹 1.概述 2.亮点 3.使用方法 4.表单元素 4.1 基本表单元素 4.2 定制表单元素 5.经常使用函数 5.1  add_action_buttons($c ...

  4. 利用css来让一个div在页面中垂直居中的方法

    一.如何让一个div在页面中垂直居中(请至少列出三种) 1.距离页面窗口左边框和上边框的距离设置为50%,这个50%就是指页面窗口的宽度和高度的50%,最后将该DIV分别左移和上移,左移和上移的大小就 ...

  5. webp图片详解

    WebP(发音 weppy),是一种支持有损压缩和无损压缩的图片文件格式,派生自图像编码格式 VP8.根据 Google 的测试,无损压缩后的 WebP 比 PNG 文件少了 45% 的文件大小,即使 ...

  6. CentOS 7中将Tomcat设置为系统服务

    tomcat 需要增加一个pid文件,在tomca/bin 目录下面,增加 setenv.sh 配置,catalina.sh启动的时候会调用,在该文件中添加如下内容 CATALINA_PID=&quo ...

  7. Hadoop集群出现no data node to stop的解决方案

    问题描述: 今天stop hadoop集群的时候出现no datanode to stop ,寻找解决方案,并不是网上资料所说的什么DFS Used .Non DFS Used等于0 .所有的节点都是 ...

  8. 安装器---Inno Setup

    Inno Setup[1]  用Delphi写成,其官方网站同时也提供源程序免费下载.它虽不能与Installshield这类恐龙级的安装制作软件相比,但也当之无愧算是后起之秀.Inno Setup是 ...

  9. 《C程序设计语言》 squeeze函数(从字符串s中删除字符c)

    squeeze void squeeze(char string[], int ch) { int i, j; ; string[i] != '\0'; i++) { if (string[i] != ...

  10. HTML5本地存储详解

    HTML5storage提供了一种方式让网站能够把信息存储到你本地的计算机上,并再以后需要的时候进行获取.这个概念和cookie相似,区别是它是为了更大容量存储设计的.Cookie的大小是受限的,并且 ...