[WCF的Binding模型]之三:信道监听器(Channel Listener)
信道管理器是信道的创建者,一般来说信道栈的中每个信道对应着一个信道管理器。基于不同的消息处理的功能,将我们需要将相应的信道按照一定的顺序能组织起来构成一个信道栈,由于信道本身是由信道管理器创建的,所以信道对应的信道管理器也构成一个信道管理器栈,栈中信道管理器的顺序决定由它所创建信道的顺序。
对于WCF的信道层来说,信道管理器在服务端和客户端扮演着不同的角色,服务端的信道管理器在于监听来自客户端的请求,而客户端的信道仅仅是单纯的创建用于消息发送的信道。因此,客户端的消息管理器又称为信道监听器(Channel Listener),客户端的信道管理器则成为信道工厂(channel factory)。
在WCF中,所有的信道管理器,不管是位于服务端的信道监听器还是客户端的信道工厂,都继承自一个基类:System.ServiceModel.Channels.ChannelManagerBase。ChannelManagerBase直接继承自CommunicationObject,并实现了接口IDefaultCommunicationTimeouts。
public abstract class ChannelManagerBase : CommunicationObject, IDefaultCommunicationTimeouts { ...... }
1. 信道监听器(Channel Listener)
其实我们完全可以把一个WCF应用开成是一个普通的基于监听-请求模式的网络应用,服务端将监听器绑定到一个或一组URI上进行网络监听,一旦成功监听到来自客户端的请求,则接收、处理该请求,如需回复则发送回复回客户端。在整个过程中,监听器处于核心的地位,而WCF中的信道监听器就起着这样的作用。
1.1. 关于信道监听器的监听过程
熟悉网络编程的朋友一定会对套节字应用编程接口(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对象,通过该对象获取请求消息并发送回复消息。
1.2. 信道监听器相关的接口和基类
由于信道监听器是位于服务端的信道管理器,所以所有的信道监听器均继承自基类:ChannelManagerBase。同时由于信道监听器具有其特殊的请求监听的功能,所以WCF还定义一些相关的接口,比如System.ServiceModel.Channels.IChannelListener和System.ServiceModel.Channels.IChannelListener。
IChannelListener继承自ICommnucationObject接口。定义了一组WaitForChannel和BeginWaitForChannel/EndWaitForChannel以同步和异步的方式判断是否具有一个可用的信道;GetProperty和IChannel的GetProperty相对;Uri属性返回真正的监听地址。
public interface IChannelListener : ICommunicationObject { IAsyncResult BeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state); bool EndWaitForChannel(IAsyncResult result); T GetProperty() where T : class; bool WaitForChannel(TimeSpan timeout); Uri Uri { get; } }
范型类型的IChannelListener继承自IChannelListener,范型类型TChannel是一个实现了IChannel的类,一般来说,TChannel代表基于某种channel shape的Channel, 比如实现了IOutputChannel、IInputChannel、IRequestChanne、IReplyChannel、IDuplexChannel的IChannel类型。定义在IChannelListener的AcceptChannel和BeginAcceptChannel/EndAcceptChannel在连接请求被监听到时,以同步或者异步的方式创建信道栈用于消息的接收。
public interface IChannelListener : IChannelListener, ICommunicationObject where TChannel : class, IChannel { // Methods TChannel AcceptChannel(); TChannel AcceptChannel(TimeSpan timeout); IAsyncResult BeginAcceptChannel(AsyncCallback callback, object state); IAsyncResult BeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state); TChannel EndAcceptChannel(IAsyncResult result); }
除了定义两个接口外,WCF中还定义了与这两个接口向对应的抽象基类:System.ServiceModel.Channels.ChannelListenerBase和System.ServiceModel.Channels.ChannelListenerBase。ChannelListenerBase实现了接口IChannelListener,而ChannelListenerBase实现了接口IChannelListener。
public abstract class ChannelListenerBase : ChannelManagerBase, IChannelListener, ICommunicationObject { ... ... } public abstract class ChannelListenerBase : ChannelListenerBase, IChannelListener, IChannelListener, ICommunicationObject where TChannel : class, IChannel { ... ... }
图3-13所示的类图大体上表示了上述的这些基类和接口之间的关系:
图3-13 信道监听器接口与基类
1.3. 案例演示3-3:如何自定义信道监听器
在上面一节的案例演示中,我们创建了两个用于请求-回复消息交换模式下的自定义信道,一个是实现了IRequestChannel的SimpleRequestChannel.,另一个是实现了IReplyChannel的SimpleReplyChannel。在本案例以及接下来的案例演示中,我们将为这两个自定义创建两个相应的信道管理器,其实一个是用于创建SimpleRequestChannel的自定义信道工厂,另一个则是创建SimpleReplyChannel的自定义信道监听器。先来看看我们自定义的信道监听器SimpleChannelListener。该类继承自范型的ChannelListenerBase:
public class SimpleChannelListener : ChannelListenerBase where TChannel : class, IChannel { ... ... }
我们说过信道一般不会孤立地存在,而是存在于一个由多个信道按照一定顺序构成的信道栈中。由于信道管理器是信道的缔造者,要创建整个信道栈,同样需要这些信道对应的信道管理器按照相应的顺序组成一个信道管理器栈。反映在具体实现上,当执行了某个方法之后,需要调用栈中后一个信道监听器相应的方法,所以在SimpleChannelListener中,定义一个字段_innerChanneListener,代表栈中与之相邻的信道监听器。_innerChanneListener通过在构造函数中指定的BindingContext对象创建。关于BindingContext,我将在后面的一节中左详细的介绍。
public class SimpleChannelListener : ChannelListenerBase where TChannel : class, IChannel { ... ... private IChannelListener _innerChanneListener; public SimpleChannelListener(BindingContext context) { PrintHelper.Print(this, "SimpleChannelListener"); this._innerChanneListener = context.BuildInnerChannelListener(); } }
对于SimpleChannelListener来说,它的最重要的功能就是创建我们自定义的ReplyChannel:SimpleReplyChannel。SimpleReplyChannel的创建实现在OnAcceptChannel和OnEndAcceptChannel方法中。在构造SimpleReplyChannel的innerChannel通过_innerChanneListener的AcceptChannel方法创建。
public class SimpleChannelListener : ChannelListenerBase where TChannel : class, IChannel { ... ... protected override TChannel OnAcceptChannel(TimeSpan timeout) { PrintHelper.Print(this, "OnAcceptChannel"); IReplyChannel innerChannel = this._innerChanneListener.AcceptChannel(timeout) as IReplyChannel; return new SimpleReplyChannel(this, innerChannel) as TChannel; } protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state) { PrintHelper.Print(this, "OnBeginAcceptChannel"); return this._innerChanneListener.BeginAcceptChannel(timeout, callback, state); } protected override TChannel OnEndAcceptChannel(IAsyncResult result) { PrintHelper.Print(this, "OnEndAcceptChannel"); return new SimpleReplyChannel(this,this._innerChanneListener.EndAcceptChannel(result) as IReplyChannel) as TChannel; } }
对于定义在基类必须实现的抽象方法来说,为了简单起见,我们仅仅是通过PrintHelper输出当前执行的方法名称,然后调用_innerChanneListener的相应的方法就可以了:
public class SimpleChannelListener : ChannelListenerBase where TChannel : class, IChannel { ... ... protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state) { PrintHelper.Print(this, "OnBeginWaitForChannel"); return this._innerChanneListener.BeginWaitForChannel(timeout, callback, state); } protected override bool OnEndWaitForChannel(IAsyncResult result) { PrintHelper.Print(this, "OnEndWaitForChannel"); return this._innerChanneListener.EndWaitForChannel(result); } protected override bool OnWaitForChannel(TimeSpan timeout) { PrintHelper.Print(this, "OnWaitForChannel"); return this._innerChanneListener.WaitForChannel(timeout); } ... ... }
WCF中的绑定模型:
[WCF中的Binding模型]之一: Binding模型简介
[WCF中的Binding模型]之二: 信道与信道栈(Channel and Channel Stack)
[WCF中的Binding模型]之三:信道监听器(Channel Listener)
[WCF中的Binding模型]之四:信道工厂(Channel Factory)
[WCF中的Binding模型]之五:绑定元素(Binding Element)
[WCF中的Binding模型]之六:从绑定元素认识系统预定义绑定