[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模型]之六:從綁定元素認識系統預定義綁定