3.4.1 信道监听器(Channel Listener)
我们完全可以把一个WCF应用看成是一个普通的基于监听-请求模式的网络应用,服务端将监听器绑定到一个或一组URI上进行网络监听,一旦成功监听到来自客户端的请求,则接收、处理该请求,如需回复则发送回复给客户端。在整个过程中,监听器处于核心的地位,而WCF中的信道监听器就起着这样的作用。
关于信道监听器的监听过程
熟悉网络编程的朋友对套节字应用编程接口(Berkeley Sockets API)一定不会陌生,通过Socket API,我们很容易创建基于网络监听-请求的应用程序。在.NET编程环境下,将System.Net.Sockets.TcpListener或System.Net.Sockets.Socket对象绑定到一个URI上,让他们监听来自客户端的连接。当连接请求被成功监测到,调用Accept相关方法创建Socket或TcpClient对象,并通过这些对象获得请求消息。WCF中的信道监听器与之相似,但对于数据报信道(Datagram Channel)和会话信道(Sessionful Channel),其监听机制具有一些差别。
1.基于数据报信道(Datagram Channel)的监听机制
当我们对一个服务进行寄宿的时候,会为之添加一个或多个终结点。对于一个终结点来说,它具有一个代表逻辑地址的终结点地址,还有一个代表物理地址的监听地址(关于逻辑地址和物理地址,请参阅第2章),如果监听地址(ListenUri)没有显式地指定,则监听地址和逻辑地址共享相同的URI。对于每一个不同监听地址,WCF会通过具体的绑定对象创建一个信道监听器。开始监听的同时,监听信道栈会被创建出来。
一旦消息请求被成功监听,无论来自哪个客户端(服务代理),信道监听器都会使用创建好的信道栈对请求消息进行接收、处理。对于服务端来说,相同的监听地址采用同一个信道栈。
2.基于会话信道(Sessionful Channel)的监听机制
对于采用会话的场景,WCF的监听机制有所不同。由信道监听器创建的信道栈不是共享的,而是和具体的客户端一一匹配的,也就是说,不同的客户端独占地使用一个服务端信道栈。正因为如此,信道栈的创建时机选择在成功监听到消息请求之时,而不是监听的开始。
对于来自某个客户端的第一次请求,信道监听器会为之创建一个新的信道栈,并利用它来接收、处理该请求。创建的信道栈会被直接用于同一个客户端的后续请求的接收。由于WCF会为每一个活动的(Active)客户端维护一个会话信道栈,为了避免并发的信道栈过多导致服务端资源的过度占用,WCF具有最大并发会话数量的限制。一旦超过这个限制,信道将不会被创建,相应的请求也就得不到及时的处理。
也正是这个原因,对于基于会话信道的服务调用,应该及时地关闭。当客户端显式地关闭会话信道栈时,服务会接收到相应的请求,从而将服务端的会话信道关闭。
信道监听器相关编程接口
由于信道监听器是位于服务端的信道管理器,所以所有的信道监听器均继承自基类:ChannelManagerBase。WCF还定义一些相关的接口来抽象信道监听器的基本行为。典型的接口包括System.ServiceModel.Channels.IChannelListener和System.ServiceModel.Channels. IChannelListener<TChannel>。
IChannelListener继承自ICommunicationObject接口。定义了一组WaitForChannel和BeginWaitForChannel/EndWaitForChannel方法,以同步和异步的方式等待信道的抵达;GetProperty<T>和IChannel的GetProperty<T>相对;URI属性返回真正的监听地址。
public interface IChannelListener : ICommunicationObject { IAsyncResult BeginWaitForChannel(TimeSpan timeout,AsyncCallback callback, object state); bool EndWaitForChannel(IAsyncResult result); T GetProperty<T>() where T : class; bool WaitForChannel(TimeSpan timeout); Uri Uri { get; } }
IChannelListener<TChannel>继承自IChannelListener,TChannel是一个实现了IChannel接口的信道。一般来说,TChannel代表基于某种信道形状的信道, 比如实现了IOutputChannel、IInputChannel、IRequestChannel、IReplyChannel、IDuplexChannel的信道。定义在IChannelListener<TChannel>的AcceptChannel和BeginAcceptChannel/EndAcceptChannel以同步或异步的方式创建用于接收处理消息的信道栈。
public interface IChannelListener<TChannel> : IChannelListener, ICommunicationObject where TChannel : class, IChannel { 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.Channel-ListenerBase<TChannel>,它们分别实现了上面的两个接口。图3-13所示的类图大体上表示了上述这些基类和接口之间的关系:
图3-13 信道监听器接口与基类
public abstract class ChannelListenerBase : ChannelManagerBase, IChannelListener, ICommunicationObject { //省略成员 } public abstract class ChannelListenerBase<TChannel> : ChannelListenerBase, IChannelListener<TChannel>, IChannelListener, ICommunicationObject where TChannel : class, IChannel { //省略成员 }
案例演示:如何自定义信道监听器
在上一节的案例演示中创建了两个服务端信道:数据报信道SimpleReplyChannel和会话信道SimpleDuplexSessionChannel。接下来,分别为它们创建相应的信道监听器。
1.创建数据报信道监听器
先来看看自定义的信道监听器SimpleDatagramChannelListener<TChannel>,该类继承自泛型的ChannelListenerBase<TChannel>:
public class SimpleDatagramChannelListener<TChannel> : ChannelListenerBase<TChannel> where TChannel : class, IChannel { //省略成员 }
某一个信道监听器只负责某个信道的创建,整个信道栈的创建依赖于相关信道监听器的协同工作。与创建自定义信道类似,在创建自定信道监听器时,我们会为之指定一个内部信道监听器(_innerChannelListener),它代表信道栈中下一个信道对应的信道监听器。在构造函数中,_innerChannelListener通过传入的BindingContext创建。
public class SimpleDatagramChannelListener<TChannel> : ChannelListenerBase<TChannel> where TChannel : class, IChannel { //其他成员 private IChannelListener<TChannel> _innerChanneListener; public SimpleDatagramChannelListener(BindingContext context) { PrintHelper.Print(this, "SimpleDatagramChannelListener"); this._innerChanneListener = context.BuildInnerChannelListener <TChannel>(); } }
对于SimpleDatagramChannelListener<TChannel>来说,它最重要的功能就是创建我们自定义的ReplyChannel:SimpleReplyChannel。SimpleReplyChannel的创建实现在OnAcceptChannel和OnEndAcceptChannel方法中。
public class SimpleDatagramChannelListener<TChannel> : ChannelListenerBase<TChannel> 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输出相应信息之后,调用后续监听器(_innerChannelListener)的相应方法即可。
public class SimpleDatagramChannelListener<TChannel> : ChannelListenerBase<TChannel> 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); } }
2.创建会话信道监听器
接下来,我们为自定义会话信道SimpleDuplexSessionChannel创建信道监听器SimpleSessionChannelListener<TChannel>。定义完全与上面的类似,唯一须要特别说明的是,须要重写GetProperty<T>方法。
using System; using System.ServiceModel.Channels; namespace Artech.CustomChannels { public class SimpleSessionChannelListener<TChannel>: ChannelListenerBase<TChannel> where TChannel:class,IChannel { //其他成员 private IChannelListener<TChannel> _innerChanneListener; public SimpleSessionChannelListener(BindingContext context) { PrintHelper.Print(this, "SimpleSessionChannelListener"); this._innerChanneListener = context.BuildInnerChannelListener <TChannel>(); } protected override TChannel OnAcceptChannel(TimeSpan timeout) { PrintHelper.Print(this, "OnAcceptChannel"); IDuplexSessionChannel innerChannel = this._innerChanneListener. AcceptChannel(timeout) as IDuplexSessionChannel; return new SimpleDuplexSessionChannel(this, innerChannel) as TChannel; } public override T GetProperty<T>() { return this._innerChanneListener.GetProperty<T>(); } } }