接收者:通道偵聽器
和它們的名字暗示的一樣,通道偵聽器就是為了創建通道並偵聽傳入的消息 。這個模型借鑒了伯克利Socket編程API。在WCF裡,這個模型可以在 Windows Socket(Winsock) API裡看到。在.NET Framework編程裡,這個模型存在於 System.Net.Sockets命名空間裡。在這個模型裡,TcpListener或Socket會綁定 一個地址,然後被動偵聽連接傳入的消息。當連接建立以後(例如,客戶端鏈接 到偵聽器),會有一個以Accept開頭的方法返回一個TcpListener或Socket的實 例,程序可以使用這個對象來接受數據。
在WCF裡,通道偵聽器做著相同的工作。它們會綁定一個URI,然後等待傳入 的消息,當連接建立以後一個以Accept開頭的方法會返回一個通道的實例。然後 程序可以使用這個通道對象來接收消息。盡管所有的通道都定義了以Accept 開 頭的方法,但是只有偵聽器類型的偵聽器才會偵聽消息。舉例說明一下,考慮一 下通道偵聽器的堆棧。通道偵聽器處於堆棧的最底層。傳輸通道偵聽器是唯一的 一個綁定地址並且開始偵聽連接消息的通道。堆棧處於上面通道偵聽器會依次調 用下一個通道偵聽器的Accept方法,直到調用最底層的傳輸通道偵聽器。
圖7-1:傳輸通道堆棧
但是也不是所有的傳輸通道偵聽器都是這樣工作的。因為各自傳輸層固有的 差別,導致了他們之間存在很大的不同。例如,面向連接的傳輸通道偵聽器(例 如 TCP/IP和named pipe)在接收了一個消息以後會返回一個通道。面向非連接 的傳輸通道偵聽器(例如MSMQ)會立即返回一個通道,因為不需要等待請求消息 。
The IChannelListener Interface
IChannelListener接口
所有的通道都實現了System.ServiceModel.Channels.IChannelListener接口 。這個接口會強制所有的通道實現通道狀態機,而且包含一些基本的通道偵聽器 成員。IChannelListener的定義如下:
public interface IChannelListener : ICommunicationObject {
IAsyncResult BeginWaitForChannel(TimeSpan timeout,AsyncCallback callback,Object state);
Boolean EndWaitForChannel(IAsyncResult result);
Boolean WaitForChannel(TimeSpan timeout);
T GetProperty<T>() where T: class;
// the listening address
Uri Uri { get; }
}
WaitForChannel方法(包含異步方法)可以返回一個Boolean值,來表示一個 通道是否可用。Uri可以訪問偵聽地址。GetProperty<T>可以被實現的 IChannel的類使用。IChannelListener接口沒有實現IChannel,因為IChannel是 區別API裡通道的標識。例如,對於類型和IChannel接口,許多泛型參數是受約 束的。目的就是為了約束擁有特定形狀通道的參數。如果IChannelListener實現 了 Channel,通道偵聽器就可以在很多地方使用,而不是只能被通道調用,並且 堆棧和堆棧裡的通道偵聽器都必須支持查詢功能。
IChannelListener<TChannel>接口
所有的通道同樣也實現IChannelListener<TChannel>接口。這裡我們 會第一次看到伯克利Socket API的Accept范式,如下所示:
public interface IChannelListener<TChannel> : IChannelListener,
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);
}
注意,接口定義把泛型類型指定為一個具體的IChannel。在WCF API裡,實現 特定形狀的通道必須遵守這個規則。整體來看,這意味著通道偵聽器必須引用一 個通道形狀。這與通道使用通道形狀的方式不同。通道是實現通道形狀,而通道 偵聽器引用一個通道形狀並且使用這個引用來創建特定的通道。
實現IChannelListener<TChannel>接口的類型必須通過AcceptChannel 方法(包含異步實現)返回一個通道的實例。與通道層的其它成員一樣, AcceptChannel也包含一個重載的方法,它可以接受一個TimeSpan參數。因為消 息接收程序可以長時間等待接受消息,所以,參數經常是TimeSpan.MaxValue。
ChannelListenerBase類型
所有的通道偵聽器都繼承自抽象類型 System.ServiceModel.Channels.ChannelListenerBase。它的定義如下:
public abstract class ChannelListenerBase : ChannelManagerBase,
IChannelListener,
ICommunicationObject {
protected ChannelListenerBase();
protected ChannelListenerBase(IDefaultCommunicationTimeouts timeouts);
// IChannelListener implementation
public IAsyncResult BeginWaitForChannel(TimeSpan timeout,
AsyncCallback callback,
Object state);
public bool EndWaitForChannel(IAsyncResult result);
public bool WaitForChannel(TimeSpan timeout);
// Extensibility points for IChannelListener members
protected abstract IAsyncResult OnBeginWaitForChannel(TimeSpan timeout,
AsyncCallback callback, Object state);
protected abstract bool OnEndWaitForChannel(IAsyncResult result);
protected abstract bool OnWaitForChannel(TimeSpan timeout);
public abstract Uri Uri { get; }
// Query mechanism
public virtual T GetProperty<T>() where T: class;
// CommunicationObject timeouts
protected override TimeSpan DefaultCloseTimeout { get; }
protected override TimeSpan DefaultOpenTimeout { get; }
// ChannelManagerBase timeouts
protected override TimeSpan DefaultReceiveTimeout { get; }
protected override TimeSpan DefaultSendTimeout { get; }
}
這裡非常有趣的一點就是,有一個構造函數接受一個TimeSpan參數。由於 ChannelListenerBase的類型層次的原因,它定義了4個protected的TimeSpan屬 性。WCF 類型系統對每個超時屬性默認設置的值都是1分鐘。如果這個值不能滿 足通道(包括子通道)的需求,你可以通過ChannelListenerBase構造函數傳遞 一個IDefaultCommunicationTimeouts參數。在這個構造函數裡,超時的值會設 置到每個相關的屬性上。你會在第8章“綁定”裡看到Binding實現了 IDefaultCommunicationTimeouts接口,而且這個也是用戶控制通道超時屬性的 一種方式。
ChannelListenerBase<TChannel>類型
通道偵聽器也繼承自抽象類型 System.ServiceModel.Channels.ChannelListenerBase<TChannel>。這個 類型是ChannelListenerBase的子類型,而且實現了 IChannelListener<TChannel>接口,如下所示:
public abstract class ChannelListenerBase<TChannel> : ChannelListenerBase,
IChannelListener<TChannel>, where TChannel: class, IChannel {
protected ChannelListenerBase();
protected ChannelListenerBase(IDefaultCommunicationTimeouts timeouts);
// IChannelListener<TChannel> implementation
public IAsyncResult BeginAcceptChannel(AsyncCallback callback,
Object state);
public IAsyncResult BeginAcceptChannel(TimeSpan timeout,
AsyncCallback callback, Object state);
public TChannel EndAcceptChannel(IAsyncResult result);
public TChannel AcceptChannel();
public TChannel AcceptChannel(TimeSpan timeout);
// extensibility points for IChannelListener<TChannel>
protected abstract TChannel OnAcceptChannel(TimeSpan timeout);
protected abstract IAsyncResult OnBeginAcceptChannel(TimeSpan timeout,
AsyncCallback callback, Object state);
protected abstract TChannel OnEndAcceptChannel(IAsyncResult result);
}
創建自定義通道偵聽器
既然已經學習完了通道偵聽器裡使用的類型,現在我們自己來創建一個通道 偵聽器。前面一章裡,我們學習了如何構建不同的DelegatorChannel通道。這一 節裡,我們會學習如何在消息接受程序端創建一個通道偵聽器,而且這些通道偵 聽器能夠創建DelegatorChannel通道。當然這些代碼直到第8章才能全部給出。
當創建一個通道偵聽器的時候,我們首先考慮的就是要支持的通道形狀。因 為我們的DelegatorChannel例子要支持任何一種通道形狀,所以我們的通道偵聽 器必須能夠創建所有已知的DelegatorChannel通道。在第6章裡,我們使用泛型 參數,這給我們的編碼帶來了很大的靈活性,這裡我們也會這麼做。
我們就從熟悉的地方開始。這裡最簡單的方法就是繼承 ChannelListenerBase<TChannel>類型。我們必須使得我們的通道偵聽器 類型支持泛型和任何可能的通道形狀。定義如下:
internal sealed class DelegatorChannelListener<TShape> :
ChannelListenerBase<TShape> where TShape : class, IChannel {
// implementation omitted for clarity
}
注意這裡修改了DelegatorChannelListener<TShape>類型的訪問控制 器。和第6章裡的通道定義一樣,這裡的通道偵聽器不需要外部調用者訪問。我 們會在第8章裡介紹通過Binding和BindingElement對象訪問此對象。現在我們已 經定義完了通道偵聽器的基類型,現在就來實現它。下面是 DelegatorChannelListener<TShape>的具體實現:
internal sealed class DelegatorChannelListener<TShape> :
ChannelListenerBase<TShape> where TShape : class, IChannel {
// field referencing the next channel listener
IChannelListener<TShape> _innerListener;
// String to output to console
String _consolePrefix = "LISTENER: DelegatorChannelListener";
// builds the next channel listener, then assigns it to
// the _innerListener field
public DelegatorChannelListener(BindingContext context) {
PrintHelper.Print(_consolePrefix, "ctor");
this._innerListener = context.BuildInnerChannelListener<TShape>();
}
// Creates a DelegatorChannel of the correct shape and returns it
private TShape WrapChannel(TShape innerChannel) {
if(innerChannel == null) {
throw new ArgumentNullException("innerChannel cannot be null", "innerChannel");
}
if(typeof(TShape) == typeof(IInputChannel)) {
return (TShape)(Object)new DelegatorInputChannel<IInputChannel>(this,
(IInputChannel)innerChannel, "RECEIVE");
}
if(typeof(TShape) == typeof(IReplyChannel)) {
return (TShape)(object)new DelegatorReplyChannel (this, (IReplyChannel)innerChannel,
"RECEIVE");
}
if(typeof(TShape) == typeof(IDuplexChannel)) {
return (TShape)(object)new DelegatorDuplexChannel(this, (IDuplexChannel)innerChannel,
"RECEIVE");
}
if(typeof(TShape) == typeof(IInputSessionChannel)) {
return (TShape)(object)new DelegatorInputSessionChannel(this,
(IInputSessionChannel)innerChannel, "RECEIVE");
}
if(typeof(TShape) == typeof(IReplySessionChannel)) {
return (TShape)(object)new DelegatorReplySessionChannel(this,
(IReplySessionChannel)innerChannel, "RECEIVE");
}
if(typeof(TShape) == typeof(IDuplexSessionChannel)) {
return (TShape)(object)new DelegatorDuplexSessionChannel(this,
(IDuplexSessionChannel)innerChannel, "RECEIVE");
}
// cannot wrap this channel
throw new ArgumentException(String.Format("invalid channel shape passed:{0}",
innerChannel.GetType()));
}
// IChannelListener<TChannel> members
protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback
callback, object state) {
PrintHelper.Print(_consolePrefix, "OnBeginAcceptChannel");
return this._innerListener.BeginAcceptChannel(timeout, callback, state);
}
protected override TShape OnEndAcceptChannel(IAsyncResult result) {
// create and return the channel
PrintHelper.Print(_consolePrefix, "OnEndAcceptChannel");
TShape innerChannel = _innerListener.EndAcceptChannel (result);
// when closing, _inner.EndAcceptChannel returns null, nothing to wrap
if (innerChannel != null) {
return WrapChannel(innerChannel);
}
return null;
}
protected override TShape OnAcceptChannel(TimeSpan timeout){
// delegate to next channel, wrap it, and return it
PrintHelper.Print(_consolePrefix, "OnAcceptChannel");
TShape innerChannel = _innerListener.AcceptChannel (timeout);
// when closing, _inner.AcceptChannel returns null, nothing to wrap
if (innerChannel != null) {
return WrapChannel(innerChannel);
}
return null;
}
// IChannelListener members
protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback
callback, object state) {
PrintHelper.Print(_consolePrefix, "OnBeginWaitForChannel");
return this._innerListener.BeginWaitForChannel(timeout, callback, state);
}
protected override bool OnEndWaitForChannel(IAsyncResult result) {
PrintHelper.Print(_consolePrefix, "OnEndWaitForChannel");
return this._innerListener.EndWaitForChannel(result);
}
protected override bool OnWaitForChannel(TimeSpan timeout) {
PrintHelper.Print(_consolePrefix, "OnWaitForChannel");
return this._innerListener.WaitForChannel(timeout);
}
public override Uri Uri {
get {
PrintHelper.Print(_consolePrefix, "Uri");
return this._innerListener.Uri;
}
}
public override T GetProperty<T>() {
PrintHelper.Print(_consolePrefix, "GetProperty<" + typeof(T) + ">");
return this._innerListener.GetProperty<T>();
}
// CommunicationObject members
protected override void OnAbort() {
PrintHelper.Print(_consolePrefix, "OnAbort");
this._innerListener.Abort();
}
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback,
object state) {
PrintHelper.Print(_consolePrefix, "OnBeginClose");
return this._innerListener.BeginClose(timeout, callback, state);
}
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback,
object state) {
PrintHelper.Print(_consolePrefix, "OnBeginOpen");
return this._innerListener.BeginOpen(timeout, callback, state);
}
protected override void OnClose(TimeSpan timeout) {
PrintHelper.Print(_consolePrefix, "OnClose");
this._innerListener.Close(timeout);
}
protected override void OnEndClose(IAsyncResult result) {
PrintHelper.Print(_consolePrefix, "OnEndClose");
this._innerListener.EndClose(result);
}
protected override void OnEndOpen(IAsyncResult result) {
PrintHelper.Print(_consolePrefix, "OnEndOpen");
this._innerListener.EndOpen(result);
}
protected override void OnOpen(TimeSpan timeout) {
PrintHelper.Print(_consolePrefix, "OnOpen");
this._innerListener.Open(timeout);
}
}
這裡要補充一下。從構造函數開始。和前面幾章裡DelegatorChannel的定義 一樣,DelegatorChannelListener<TShape>對象可以與其它通道偵聽器在 堆棧裡共存。雖然有好幾種建立通道偵聽器堆棧的方法,但是最終都是傳輸通道 偵聽器位於堆棧的底部。DelegatorChannelListener<TShape> 類型定義 了幾個IChannelListener<TShape>類型的成員,而且可以通過構造函數賦 值。在第8章裡會介紹,Binding 創建通道偵聽器堆棧的主要方式就是使用 BindingContext對象。另外一個方法就是改變構造函數的參數類型為 IChannelListener<TShape>。這時調用者負責使用BindingContext對象。 我個人認為,這個差別問題不大。
DelegatorChannelListener<TShape>裡的大部分方法都和 DelegatorChannel裡的方法很相似,它們都是簡單地調用堆棧裡的下一個方法。 有趣的是,DelegatorChannelListener<TShape>的私有方法WrapChannel 。這個方法的作用就是返回擁有 TShape 泛型參數的DelegatorChannel的實例。 innerChannel參數傳遞給DelegatorChannel的構造函數,這樣就可以正常地創建 通道堆棧。OnAcceptChannel 和OnEndAcceptChannel是僅有的調用WrapChannel 的方法。在調用之前,它們必須調用成員變量innerListener的方法(分別調用 AcceptChannel 和EndAcceptChannel)並且把通道偵聽器傳遞給WrapChannel方 法。
當通道偵聽器堆棧關閉的時候,DelegatorChannelListener<TShape> 類型會循環調用下一個通道偵聽器的關閉方法(比如,Close、OnClose、Abort 和OnAbort)。如果在關閉方法以前調用了BeginAcceptChannel或AcceptChannel ,委托調用就會返回null。這種情況下,OnEndAcceptChannel或AcceptChannel 方法也會返回null。