共用方式為


瞭解狀態變更

本主題說明通道具有的狀態和轉換、用來建構通道狀態的型別,以及如何實作。

狀態機器和通道

處理通訊的物件 (例如通訊端) 通常會呈現狀態轉換與配置網路資源、建立或接受連線、關閉連線和終止通訊等相關的狀態機器。通道狀態機器提供了通訊物件狀態的統一模型,摘要該物件的基礎實作。ICommunicationObject 介面提供一組狀態、狀態轉換方法和狀態轉換事件。所有通道、通道處理站和通道接聽項都會實作通道狀態機器。

事件 Closed、Closing、Faulted、Opened 和 Opening 會在狀態轉換發生後對外部觀察器發出信號。

方法 Abort、Close 和 Open (以及它們的非同步對等用法) 會造成狀態轉換。

狀態屬性會傳回 CommunicationState 所定義的目前狀態:

ICommunicationObject、CommunicationObject、狀態和狀態轉換

ICommunicationObject 一開始處於 Created 狀態,在這個狀態下可設定各種屬性。一旦在 Opened 狀態下,物件就不能用於傳送和接收訊息,它的屬性也會被視為不變的。一旦在 Closing 狀態下,物件就無法再處理新的傳送或接收要求,但是直到 Close 逾時之前現有的要求仍有機會完成。如果發生無法修復的錯誤,物件會轉換為 Faulted 狀態,在這個狀態下,可檢查物件取得錯誤相關資訊,最後關閉物件。在 Closed 狀態下,物件本質上已經達到狀態機器結尾。一旦物件從某個狀態轉換為下一個狀態,就不會回到上一個狀態。

下圖說明 ICommunicationObject 狀態和狀態轉換。藉由呼叫下列三個方法其中一個,可以造成狀態轉換:Abort、Open 或 Close。此外,藉由呼叫其他實作特定的方法,也可以造成狀態轉換。當開啟通訊物件的時候或之後發生錯誤,則會轉換為 Faulted 狀態。

每個 ICommunicationObject 一開始都處於 Created 狀態。在這個狀態下,應用程式可以設定物件屬性。一旦物件處於 Created 以外的狀態,就會被視為不變的。

通道狀態轉換

Windows Communication Foundation (WCF) 提供了一個名為 CommunicationObject,會實作 ICommunicationObject 和通道狀態機器的抽象基底類別。下圖是 CommunicationObject 特定的狀態修訂圖。除了 ICommunicationObject 狀態機器之外,它還說明了叫用其他 CommunicationObject 方法的時機。

狀態變更

ICommunicationObject 事件

CommunicationObject 會公開 ICommunicationObject 所定義的五個事件。這些事件是為使用通訊物件以得知狀態轉換的程式碼而設計。如上面圖 2 所示,在物件狀態轉換為依事件所指名的狀態之後,每個事件都會引發一次。所有五個事件都屬於 EventHandler 型別,定義為:

public delegate void EventHandler(object sender, EventArgs e);

CommunicationObject 實作中,CommunicationObject 本身是傳送者,或者傳入 CommunicationObject 建構函式當做傳送者 (如果使用該建構函式多載的話)。EventArgs 參數 e 一定是 EventArgs.Empty

衍生的物件回呼

除了五個事件之外,CommunicationObject 還會宣告八個受保護的虛擬方法,設計用於狀態轉換前後回呼衍生的物件。

System.ServiceModel.Channels.CommunicationObject.OpenSystem.ServiceModel.Channels.CommunicationObject.Close 方法各有三個這類的回呼。例如,System.ServiceModel.Channels.CommunicationObject.Open 有對應的 System.ServiceModel.Channels.CommunicationObject.OnOpeningSystem.ServiceModel.Channels.CommunicationObject.OnOpen(System.TimeSpan)System.ServiceModel.Channels.CommunicationObject.OnOpenedSystem.ServiceModel.Channels.CommunicationObject.Close 有相關聯的 System.ServiceModel.Channels.CommunicationObject.OnClose(System.TimeSpan)System.ServiceModel.Channels.CommunicationObject.OnClosingSystem.ServiceModel.Channels.CommunicationObject.OnClosed 方法。

同樣的,System.ServiceModel.Channels.CommunicationObject.Abort 方法也有對應的 System.ServiceModel.Channels.CommunicationObject.OnAbort

雖然 System.ServiceModel.Channels.CommunicationObject.OnOpen(System.TimeSpan)System.ServiceModel.Channels.CommunicationObject.OnClose(System.TimeSpan)System.ServiceModel.Channels.CommunicationObject.OnAbort 沒有預設實作,但其他回呼仍有狀態機器正確性需要的預設實作。如果您要覆寫這些方法,請務必呼叫基底實作或正確地將它取代。

System.ServiceModel.Channels.CommunicationObject.OnOpening, System.ServiceModel.Channels.CommunicationObject.OnClosingSystem.ServiceModel.Channels.CommunicationObject.OnFaulted 會引發對應的 System.ServiceModel.Channels.CommunicationObject.OpeningSystem.ServiceModel.Channels.CommunicationObject.ClosingSystem.ServiceModel.Channels.CommunicationObject.Faulted 事件。System.ServiceModel.Channels.CommunicationObject.OnOpenedSystem.ServiceModel.Channels.CommunicationObject.OnClosed 會將物件狀態分別設定為 Opened 和 Closed,然後引發對應的 System.ServiceModel.Channels.CommunicationObject.OpenedSystem.ServiceModel.Channels.CommunicationObject.Closed 事件。

狀態轉換方法

CommunicationObject 提供了 Abort、Close 和 Open 實作。此外,還提供 Fault 方法,會將狀態轉換為 Faulted 狀態。圖 2 說明 ICommunicationObject 狀態機器,其中每個轉換都由造成轉換的方法所標記 (在造成上一個已標記轉換的方法實作內部,會發生未標記的轉換)。

ms789041.note(zh-tw,VS.90).gif注意:
CommunicationObject 所有的通訊狀態取得/設定實作,都是執行緒同步。

建構函式

CommunicationObject 提供了三個都會將物件置於 Created 狀態的建構函式。這些建構函式定義為:

第一個是預設建構函式,會委派至接受物件的建構函式多載:

protected CommunicationObject() : this(new object()) { … }

在同步存取通訊物件狀態時,接受物件的建構函式會將該參數做為要鎖定的物件:

protected CommunicationObject(object mutex) { … }

最後,當引發 ICommunicationObject 事件時,第三個建構函式會接受其他參數做為傳送者引數。

protected CommunicationObject(object mutex, object eventSender) { … }

前兩個建構函式會設定此傳送者。

Open 方法

前置條件:狀態為 Created。

後置條件:狀態為 Opened 或 Faulted。可能會擲回例外狀況。

Open() 方法會嘗試開啟通訊物件,並將狀態設定為 Opened。如果發生錯誤,則會將狀態設定為 Faulted。

此方法會先檢查目前狀態是否為 Created。如果目前狀態為 Opening 或 Opened,則會擲回 InvalidOperationException。如果目前狀態為 Closing 或 Closed,且物件已終止,則會擲回 CommunicationObjectAbortedException,否則會擲回 ObjectDisposedException。如果目前狀態為 Faulted,則會擲回 CommunicationObjectFaultedException

然後,將狀態設定為 Opening,並依序呼叫 OnOpening() (這會引發 Opening 事件)、OnOpen() 和 OnOpened()。OnOpened() 會將狀態設定為 Opened,並且引發 Opened 事件。如果任何這些呼叫擲回例外狀況,則 Open() 會呼叫 Fault(),並且將例外狀況反昇。下圖詳細說明 Open 處理序。

狀態變更

Close 方法

前置條件:無。

後置條件:狀態為 Closed。可能會擲回例外狀況。

在任何狀態下,都可以呼叫 Close() 方法。這個方法會嘗試依正常程序關閉物件。如果發生錯誤,則會終止物件。如果目前狀態為 Closing 或 Closed,這個方法不會有任何作用。否則,會將狀態設定為 Closing。如果原始狀態為 Created、Opening 或 Faulted,則會呼叫 Abort() (請參閱下圖)。如果原始狀態為 Opened,則會依序呼叫 OnClosing() (這會引發 Closing 事件)、OnClose() 和 OnClosed()。如果任何這些呼叫擲回例外狀況,則 Close() 會呼叫 Abort(),並且將例外狀況反昇。OnClosed() 會將狀態設定為 Closed,並且引發 Closed 事件。下圖詳細說明 Close 處理序。

狀態變更

中止

前置條件:無。
後置條件:狀態為 Closed。可能會擲回例外狀況。

如果目前狀態為 Closed,或者物件已經終止 (例如,可能藉由在另一個執行緒上執行 Abort()),則 Abort() 方法不會有任何作用。否則,方法會將狀態設定為 Closing,並且依序呼叫 OnClosing() (這會引發 Closing 事件)、OnAbort() 和 OnClosed() (不會呼叫 OnClose,因為物件正在終止,而未關閉)。OnClosed() 會將狀態設定為 Closed,並且引發 Closed 事件。如果任何這些呼叫擲回例外狀況,則會將例外狀況重新擲回至 Abort 的呼叫者。OnClosing()、OnClosed() 和 OnAbort() 實作不應封鎖 (例如,於輸入/輸出時)。下圖詳細說明 Abort 處理序。

狀態變更

Fault

Fault 方法是 CommunicationObject 特有的,不是 ICommunicationObject 介面的一部分。為求完整性,將它一併併入。

前置條件:無。

後置條件:狀態為 Faulted。可能會擲回例外狀況。

如果目前狀態為 Faulted 或 Closed,Fault() 方法不會有任何作用。否則,會將狀態設定為 Faulted 並呼叫 OnFaulted(),而引發 Faulted 事件。如果 OnFaulted 擲回例外狀況,則會重新擲回例外狀況。

ThrowIfXxx 方法

CommunicationObject 有三個受保護的方法,當物件處於特定狀態時,可用來擲回例外狀況。

如果狀態為 Closing、Closed 或 Faulted,ThrowIfDisposed 會擲回例外狀況。

如果狀態不是 Created,ThrowIfDisposedOrImmutable 會擲回例外狀況。

如果狀態不是 Opened,ThrowIfDisposedOrNotOpen 會擲回例外狀況。

擲回的例外狀況取決於狀態。下表說明不同狀態,以及在狀態下藉由呼叫 ThrowIfXxx 擲回的對應例外狀況型別。

狀態 是否已呼叫 Abort? 例外狀況

Created

N/A

System.InvalidOperationException

Opening

N/A

System.InvalidOperationException

Opened

N/A

System.InvalidOperationException

Closing

System.ServiceModel.CommunicationObjectAbortedException

Closing

System.ObjectDisposedException

Closed

在先前明確呼叫 Abort 而關閉物件的情況下,為 System.ServiceModel.CommunicationObjectAbortedException。如果在此物件上呼叫 Close,則會擲回 System.ObjectDisposedException

Closed

System.ObjectDisposedException

Faulted

N/A

System.ServiceModel.CommunicationObjectFaultedException

逾時

我們所討論的數個方法會接受逾時參數,包括 Close、Open (某些多載和非同步版本)、OnClose 和 OnOpen。這些方法的設計考慮到漫長作業 (例如,於輸入/輸出時封鎖,同時依正常程序關閉連線),因此逾時參數會表示中斷前這類作業所需的時間。任何這些方法的實作都應該使用提供的逾時值,以確保在逾時前傳回至呼叫者。不接受逾時的其他方法實作,不是為漫長作業而設計,且不應於輸入/輸出時封鎖。

但是 Open() 和 Close() 多載例外,這些方法多載不會接受逾時,而是使用衍生類別所提供的預設逾時值。CommunicationObject 會公開兩個名為 DefaultCloseTimeoutDefaultOpenTimeout 的受保護抽象屬性,定義為:

protected abstract TimeSpan DefaultCloseTimeout { get; }

protected abstract TimeSpan DefaultOpenTimeout { get; }

衍生類別會實作這些屬性,為不接受逾時值的 Open() 和 Close() 多載提供預設逾時。然後,Open() 和 Close() 實作會委派至接受逾時的多載,以傳遞預設逾時值,例如:

public void Open()

{

this.Open(this.DefaultOpenTimeout);

}

IDefaultCommunicationTimeouts

這個介面有四個唯讀屬性,為 Open、Send、Receive 和 Close 提供預設逾時值。每個實作都負責以適當方式取得預設值。為提供便利,ChannelFactoryBaseChannelListenerBase 會將這些值都預設為 1 分鐘。