专栏首页大内老A[WCF的Binding模型]之三:信道监听器(Channel Listener)

[WCF的Binding模型]之三:信道监听器(Channel Listener)

信道管理器是信道的创建者,一般来说信道栈的中每个信道对应着一个信道管理器。基于不同的消息处理的功能,将我们需要将相应的信道按照一定的顺序能组织起来构成一个信道栈,由于信道本身是由信道管理器创建的,所以信道对应的信道管理器也构成一个信道管理器栈,栈中信道管理器的顺序决定由它所创建信道的顺序。

对于WCF的信道层来说,信道管理器在服务端和客户端扮演着不同的角色,服务端的信道管理器在于监听来自客户端的请求,而客户端的信道仅仅是单纯的创建用于消息发送的信道。因此,客户端的消息管理器又称为信道监听器(Channel Listener),客户端的信道管理器则成为信道工厂(channel factory)。

在WCF中,所有的信道管理器,不管是位于服务端的信道监听器还是客户端的信道工厂,都继承自一个基类:System.ServiceModel.Channels.ChannelManagerBase。ChannelManagerBase直接继承自CommunicationObject,并实现了接口IDefaultCommunicationTimeouts。

   1: public abstract class ChannelManagerBase : CommunicationObject, IDefaultCommunicationTimeouts 
   2: { 
   3:    // ...... 
   4: }

其实我们完全可以把一个WCF应用开成是一个普通的基于监听-请求模式的网络应用,服务端将监听器绑定到一个或一组URI上进行网络监听,一旦成功监听到来自客户端的请求,则接收、处理该请求,如需回复则发送回复回客户端。在整个过程中,监听器处于核心的地位,而WCF中的信道监听器就起着这样的作用。

一、关于信道监听器的监听过程

熟悉网络编程的朋友一定会对套节字应用编程接口(Berkeley Sockets API)不会陌生,通过Socket API,我们很容易的创建基于网络监听-请求的应用程序。在.NET编程环境下,我们将System.Net.Sockets.TcpListener 或者System.Net.Sockets.Socket 对象绑定到一个URI上,让他们监听来自客户端的连接。当连接请求被成功监测到,调用Accept相关方法或者方法创建一Socket或者TcpClient对象,并通过这些对象获得请求消息。

WCF中的信道监听器与之相似。当我们对一个服务进行寄宿的时候,会为之添加一个或者多个终结点。对于一个终结点来说,它具有一个代表逻辑地址的终结点地址,还有一个代表物理地址的监听地址(关于逻辑地址和物理地址,请参阅第二章),如果监听地址(ListenUri)没有显式地指定,则监听地址和逻辑地址共享相同的URI。对于每一个不同监听地址,WCF会通过具体的绑定对象创建一个信道监听器。信道监听器通过调用AcceptChannel创建监听信道栈,位于信道栈的第一个信道被成功返回。

一旦消息请求被成功监听,如果该信道是InputChannel(数据报MEP) 或者DuplexChannel(双工MEP),则调用Receive或者BeginReceive方法接收消息,如果需要向对象发送消息,则通过Send或者BeginSend将消息发给请求者;如果信道是ReplyChannel(请求/回复MEP)则调用ReceiveRequest方法获得一个RequestContext对象,通过该对象获取请求消息并发送回复消息。

二、信道监听器相关的接口和基类

由于信道监听器是位于服务端的信道管理器,所以所有的信道监听器均继承自基类:ChannelManagerBase。同时由于信道监听器具有其特殊的请求监听的功能,所以WCF还定义一些相关的接口,比如System.ServiceModel.Channels.IChannelListener和System.ServiceModel.Channels.IChannelListener<TChannel>。

IChannelListener继承自ICommnucationObject接口。定义了一组WaitForChannel和BeginWaitForChannel/EndWaitForChannel以同步和异步的方式判断是否具有一个可用的信道;GetProperty<T>和IChannel的GetProperty<T>相对;Uri属性返回真正的监听地址。

   1: public interface IChannelListener : ICommunicationObject
   2: {
   3:     IAsyncResult BeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state);
   4:     bool EndWaitForChannel(IAsyncResult result);
   5:     T GetProperty<T>() where T : class;
   6:     bool WaitForChannel(TimeSpan timeout);
   7:  
   8:     Uri Uri { get; }
   9: }

范型类型的IChannelListener<TChannel>继承自IChannelListener,范型类型TChannel是一个实现了IChannel的类,一般来说,TChannel代表基于某种channel shape的Channel, 比如实现了IOutputChannel、IInputChannel、IRequestChanne、IReplyChannel、IDuplexChannel的IChannel类型。定义在IChannelListener<TChannel>的AcceptChannel和BeginAcceptChannel/EndAcceptChannel在连接请求被监听到时,以同步或者异步的方式创建信道栈用于消息的接收。

   1: public interface IChannelListener<TChannel> : IChannelListener, ICommunicationObject where TChannel : class, IChannel
   2: {
   3:     // Methods
   4:     TChannel AcceptChannel();
   5:     TChannel AcceptChannel(TimeSpan timeout);
   6:     IAsyncResult BeginAcceptChannel(AsyncCallback callback, object state);
   7:     IAsyncResult BeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state);
   8:     TChannel EndAcceptChannel(IAsyncResult result);
   9: }  

除了定义两个接口外,WCF中还定义了与这两个接口向对应的抽象基类:System.ServiceModel.Channels.ChannelListenerBase和System.ServiceModel.Channels.ChannelListenerBase<TChannel>。ChannelListenerBase实现了接口IChannelListener,而ChannelListenerBase<TChannel>实现了接口IChannelListener<TChannel>。

   1: public abstract class ChannelListenerBase : ChannelManagerBase, IChannelListener, ICommunicationObject
   2: { 
   3:     //... ...
   4: }
   5: public abstract class ChannelListenerBase<TChannel> : ChannelListenerBase, IChannelListener<TChannel>, IChannelListener, ICommunicationObject where TChannel : class, IChannel
   6: {
   7:     //... ...
   8: }

图1所示的类图大体上表示了上述的这些基类和接口之间的关系:

图1 信道监听器接口与基类

三、案例演示:如何自定义信道监听器

在上面一节的案例演示中,我们创建了两个用于请求-回复消息交换模式下的自定义信道,一个是实现了IRequestChannel的SimpleRequestChannel.,另一个是实现了IReplyChannel的SimpleReplyChannel。在本案例以及接下来的案例演示中,我们将为这两个自定义创建两个相应的信道管理器,其实一个是用于创建SimpleRequestChannel的自定义信道工厂,另一个则是创建SimpleReplyChannel的自定义信道监听器。先来看看我们自定义的信道监听器SimpleChannelListener<TChannel>。该类继承自范型的ChannelListenerBase<TChannel>:

   1: public class SimpleChannelListener<TChannel> : ChannelListenerBase<TChannel> where TChannel : class, IChannel
   2: {
   3:     //... ...
   4: }

我们说过信道一般不会孤立地存在,而是存在于一个由多个信道按照一定顺序构成的信道栈中。由于信道管理器是信道的缔造者,要创建整个信道栈,同样需要这些信道对应的信道管理器按照相应的顺序组成一个信道管理器栈。反映在具体实现上,当执行了某个方法之后,需要调用栈中后一个信道监听器相应的方法,所以在SimpleChannelListener<TChannel>中,定义一个字段_innerChanneListener,代表栈中与之相邻的信道监听器。_innerChanneListener通过在构造函数中指定的BindingContext对象创建。关于BindingContext,我将在后面的一节中左详细的介绍。

   1: public class SimpleChannelListener<TChannel> : ChannelListenerBase<TChannel> where TChannel : class, IChannel
   2: {
   3:     ... ...
   4:     private IChannelListener<TChannel> _innerChanneListener; 
   5:  
   6:     public SimpleChannelListener(BindingContext context)
   7:     {
   8:         PrintHelper.Print(this, "SimpleChannelListener");
   9:         this._innerChanneListener = context.BuildInnerChannelListener<TChannel>();
  10:     }
  11: }

对于SimpleChannelListener<TChannel>来说,它的最重要的功能就是创建我们自定义的ReplyChannel:SimpleReplyChannel。SimpleReplyChannel的创建实现在OnAcceptChannel和OnEndAcceptChannel方法中。在构造SimpleReplyChannel的innerChannel通过_innerChanneListener的AcceptChannel方法创建。

   1: public class SimpleChannelListener<TChannel> : ChannelListenerBase<TChannel> where TChannel : class, IChannel
   2: {
   3:     ... ... 
   4:  
   5:     protected override TChannel OnAcceptChannel(TimeSpan timeout)
   6:     {
   7:         PrintHelper.Print(this, "OnAcceptChannel");
   8:         IReplyChannel innerChannel = this._innerChanneListener.AcceptChannel(timeout) as IReplyChannel;
   9:         return new SimpleReplyChannel(this, innerChannel) as TChannel;
  10:     }
  11:     protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state)
  12:     {
  13:         PrintHelper.Print(this, "OnBeginAcceptChannel");
  14:         return this._innerChanneListener.BeginAcceptChannel(timeout, callback, state); 
  15:  
  16:     } 
  17:  
  18:     protected override TChannel OnEndAcceptChannel(IAsyncResult result)
  19:     {
  20:         PrintHelper.Print(this, "OnEndAcceptChannel");
  21:         return new  SimpleReplyChannel(this,this._innerChanneListener.EndAcceptChannel(result) as IReplyChannel) as TChannel;
  22:     }
  23: }

对于定义在基类必须实现的抽象方法来说,为了简单起见,我们仅仅是通过PrintHelper输出当前执行的方法名称,然后调用_innerChanneListener的相应的方法就可以了: 

   1: public class SimpleChannelListener<TChannel> : ChannelListenerBase<TChannel> where TChannel : class, IChannel
   2: {
   3:       protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state)
   4:         {
   5:             PrintHelper.Print(this, "OnBeginWaitForChannel");
   6:             return this._innerChanneListener.BeginWaitForChannel(timeout, callback, state); 
   7:         } 
   8:         protected override bool OnEndWaitForChannel(IAsyncResult result)
   9:         {
  10:             PrintHelper.Print(this, "OnEndWaitForChannel");
  11:             return this._innerChanneListener.EndWaitForChannel(result);
  12:         } 
  13:         protected override bool OnWaitForChannel(TimeSpan timeout)
  14:         {
  15:             PrintHelper.Print(this, "OnWaitForChannel");
  16:             return this._innerChanneListener.WaitForChannel(timeout);
  17:         }
  18:     //... ...
  19: }
  20:  
  21:  

WCF中的绑定模型: [WCF中的Binding模型]之一: Binding模型简介 [WCF中的Binding模型]之二: 信道与信道栈(Channel and Channel Stack) [WCF中的Binding模型]之三:信道监听器(Channel Listener) [WCF中的Binding模型]之四:信道工厂(Channel Factory) [WCF中的Binding模型]之五:绑定元素(Binding Element) [WCF中的Binding模型]之六:从绑定元素认识系统预定义绑定

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 《WCF的绑定模型》博文系列汇总[共6篇]

    从整个基础构架的层次结构上讲,WCF可以分成两个部分:服务模型层(Service Mode Layer)和信道层(Channel Layer)。服务模型层建立在...

    蒋金楠
  • WCF中的Binding模型之一: Binding模型简介

    一、 信道层与服务模型层(Channel Layer and Service Mode Layer) 对于一个分布式应用的开发与设计来说,通信问题是不得不考虑,...

    蒋金楠
  • WCF中的Binding模型之一: Binding模型简介

    一、 信道层与服务模型层(Channel Layer and Service Mode Layer) 对于一个分布式应用的开发与设计来说,通信问题是不得不考虑,...

    蒋金楠
  • Spring IOC概念

    反转是中思想,将类的实例化和管理交给Spring(包括关系处理垃圾回收等),我们只需要去Spring这个容器中取即可

    大话swift
  • 重新定义时间轴

    最近读了Reid Havens在PowerPivotPro上发表的一篇《产品上线时间后比较表现》的文章,不同产品上线的时间不同,通过自定义时间轴来把所有产品的上...

    公众号PowerBI大师
  • 逆向分析智能窗帘频射协议

    近来我热衷于对家庭自动化设备的破解,然后将它们添加到我的Homekit集成包之中。这事情要从几个月前说起,当时我爸订购了大批量的RAEX 433MHz射频电动窗...

    FB客服
  • ES6之对象的扩展

    对象有一个描述对象,通过Object.getOwnPropertyDescriptor方法可以获取:

    wade
  • patternplot包:用ggplot解决你对线性填充,不!所有填充的全部幻想。

    patternplot包,提供了丰度的图形可视化填充选项,但是目前我尽然没忽悠看到一篇推文来介绍和学习这个R包的。

    王诗翔呀
  • Linux0号进程,1号进程,2号进程

    本节我们将从linux启动的第一个进程说起,以及后面第一个进程是如何启动1号进程,然后启动2号进程。然后系统中所有的进程关系图做个简单的介绍

    DragonKingZhu
  • SQL Server安全(3/11):主体和安全对象(Principals and Securables)

    在保密你的服务器和数据,防备当前复杂的攻击,SQL Server有你需要的一切。但在你能有效使用这些安全功能前,你需要理解你面对的威胁和一些基本的安全概念。这篇...

    逸鹏

扫码关注云+社区

领取腾讯云代金券