共用方式為


了解狀態變化

本主題討論通道擁有的狀態和轉換、用來建構通道狀態的類型,以及如何實作它們。

狀態機器和通道

處理通訊的物件,例如套接字,通常會呈現狀態機器,其中的狀態轉換與分配網路資源、建立或接受連線、關閉連線和終止通訊有關。 通道狀態機器會提供通訊物件狀態的統一模型,以抽象化該對象的基礎實作。 介面 ICommunicationObject 提供一組狀態、狀態轉換方法和狀態轉換事件。 所有通道、通道處理站和通道接聽程式都會實作通道狀態機器。

當發生狀態轉換後,「關閉」、「關閉中」、「發生錯誤」、「開啟」和「開啟中」的事件會向外部觀察者發出訊號。

方法 Abort、Close 和 Open(及其非同步版本)會導致狀態轉換。

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

ICommunicationObject、CommunicationObject、狀態和狀態轉換

ICommunicationObject會以 [已建立] 狀態開始,其中可以設定其各種屬性。 一旦處於開啟狀態,物件就可用於傳送和接收訊息,但其屬性會被視為不可變。 一旦處於關閉狀態,對象就無法再處理新的傳送或接收要求,但現有的要求有機會完成,直到達到 Close 逾時為止。 如果發生無法復原的錯誤,物件會轉換成錯誤狀態,以便檢查錯誤的相關信息,最後關閉。 在閉合狀態中時,物件基本上已到達狀態機器的終點。 一旦對象從某個狀態轉換到下一個狀態,它就不會回到先前的狀態。

下圖顯示 ICommunicationObject 狀態和狀態轉換。 狀態轉換的原因可能是呼叫三種方法之一:中止、開啟或關閉。 也可能是因為呼叫其他實作特定方法所造成。 由於開啟或開啟後通訊對象時的錯誤,可能會轉換至故障狀態。

每個 ICommunicationObject 都會以 [已建立] 狀態開始。 在此狀態下,應用程式可以藉由設定其屬性來設定物件。 一旦對象處於 [已建立] 以外的狀態,它就會被視為不可變。

通道狀態轉換的數據流圖表。
圖 1. ICommunicationObject 狀態機。

Windows Communication Foundation(WCF)提供名為CommunicationObject的抽象基類,實作ICommunicationObject和通道狀態機器。 下圖是 專屬於 CommunicationObject的修改狀態圖表。 除了 ICommunicationObject 狀態機器之外,它也會顯示叫用其他 CommunicationObject 方法的時間。

CommunicationObject 實作狀態變更的數據流圖表。 圖 2. ICommunicationObject 狀態計算機的 CommunicationObject 實作,包括對事件和受保護方法的呼叫。

ICommunicationObject 的事件列表

CommunicationObject 會公開 所 ICommunicationObject定義的五個事件。 這些事件是針對使用通訊物件來通知狀態轉換的程式代碼所設計。 如上圖 2 所示,在物件的狀態轉換為事件所命名的狀態之後,就會引發每個事件一次。 這五個事件都是 EventHandler 定義為下列類型的事件:

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

CommunicationObject 的實作中,傳送者可以是 CommunicationObject 本身,或者是在 CommunicationObject 建構函式中傳入作為傳送者的任何物件(如果使用了該建構函式的多載)。 EventArgs 參數 e一律 EventArgs.Empty為 。

衍生物件的回呼函式

除了五個事件之外,還宣告八個受保護的虛擬方法, CommunicationObject 其設計目的是允許在發生狀態轉換之前和之後呼叫衍生物件。

CommunicationObject.OpenCommunicationObject.Close方法各有三個這類特定回呼與它們相關聯。 例如,對應 CommunicationObject.Open 的有 CommunicationObject.OnOpeningCommunicationObject.OnOpenCommunicationObject.OnOpened。 與 CommunicationObject.Close 相關聯的是 CommunicationObject.OnCloseCommunicationObject.OnClosingCommunicationObject.OnClosed 方法。

同樣地,方法 CommunicationObject.Abort 也有對應的 CommunicationObject.OnAbort

雖然 CommunicationObject.OnOpenCommunicationObject.OnCloseCommunicationObject.OnAbort 沒有預設實作,但其他回呼確實具有狀態機器正確性所需的預設實作。 如果您覆寫這些方法,請務必呼叫基底實作或正確地取代它。

CommunicationObject.OnOpeningCommunicationObject.OnClosingCommunicationObject.OnFaulted 觸發對應的 CommunicationObject.OpeningCommunicationObject.ClosingCommunicationObject.Faulted 事件。 CommunicationObject.OnOpened 並將 CommunicationObject.OnClosed 對象狀態分別設定為 Opened 和 Closed,然後引發對應的 CommunicationObject.OpenedCommunicationObject.Closed 事件。

狀態轉換方法

CommunicationObject 提供 Abort、Close 和 Open 的實作。 它還提供一個名為 Fault 的方法,這會導致狀態轉換至 Faulted 狀態。 圖 2 顯示 ICommunicationObject 狀態機,其中每一個轉換都標記了導致該轉換的方法(未標記的轉換發生在導致最後一個有標記轉換的方法的實作內)。

備註

通訊狀態取得/設定的所有 CommunicationObject 實作都會進行線程同步處理。

建構函式

CommunicationObject 提供三個建構函式,其中所有建構函式都會讓物件保持 「已建立」狀態。 建構函式定義為:

第一個建構函式是無參數建構函式,委派給接受 對象的建構函式多載:

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

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

protected CommunicationObject(object mutex) { … }

最後,第三個建構函式會採用額外的參數,當 ICommunicationObject 事件被引發時,會用作傳送者參數。

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

前兩個建構函式會將傳送者設定為這個。

Open 方法

前置條件:狀態為 [已建立]。

後置條件:狀態已開啟或發生錯誤。 可能會拋出例外狀況。

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

方法首先會檢查目前的狀態是否為已創建(Created)。 如果目前的狀態為 開啟中 或 開啟,則會拋出 InvalidOperationException。 如果目前狀態為 Closing 或 Closed,並且物件已終止,則擲回CommunicationObjectAbortedException,否則擲回ObjectDisposedException。 如果目前狀態為 Faulted,則會擲回 CommunicationObjectFaultedException

然後,它會將狀態設定為開啟中,並依序呼叫 OnOpening()(這會引發開啟事件)、OnOpen() 和 OnOpened()。 OnOpened() 會將狀態設定為 Opened,並引發 Opened 事件。 如果其中任何一個擲回例外狀況,Open() 會呼叫 Fault(),並讓例外狀況往上傳遞。 下圖更詳細地顯示開啟流程。

ICommunicationObject.Open 狀態變更的數據流圖表。
覆寫 OnOpen 方法以實作自定義開啟邏輯,例如開啟內部通訊物件。

Close 方法

前置條件:無。

後置條件:狀態為已關閉。 可能會拋出例外狀況。

Close() 方法可以在任何狀態呼叫。 它會嘗試以正常方式關閉該物件。 如果發生錯誤,它會終止 物件。 如果目前狀態為 [關閉] 或 [已關閉],則方法不會執行任何動作。 否則,它會將狀態設定為 [關閉]。 如果原始狀態為 [已建立]、[開啟] 或 [錯誤],則會呼叫 Abort() (請參閱下圖)。 如果原始狀態為 Opened,則會按順序呼叫 OnClosing(觸發 Closing 事件)、OnClose 和 OnClosed。 如果其中任何一個擲回例外狀況,Close() 會呼叫 Abort(),並讓例外狀況冒泡上來。 OnClosed() 會將狀態設定為 Closed,並引發 Closed 事件。 下圖更詳細地顯示關閉流程的過程。

ICommunicationObject.Close 狀態變更的數據流圖表。
覆寫 OnClose 方法以實作自訂關閉邏輯,例如關閉內部通訊物件。 應該在 OnClose() 中實作所有可能長時間封鎖的正常關閉邏輯(例如,等待對方回應),因為 OnClose() 接受逾時參數,而且它不會作為 Abort() 的一部分被呼叫。

流產

前置條件:無。
後置條件:狀態為已關閉。 可能會拋出例外狀況。

如果目前狀態為 Closed,或物件已在之前終止,則 Abort() 方法不會執行任何動作(例如,可能藉由在另一個線程上執行 Abort()。 否則,它會將狀態設定為Closing,按照OnClosing()(這會引發Closing事件)、OnAbort() 和 OnClosed() 的順序進行呼叫(不會呼叫 OnClose,因為物件正在結束,而不是關閉)。 OnClosed() 會將狀態設定為 Closed,並引發 Closed 事件。 如果其中任何一個拋出例外狀況,則會將它重新拋出給中止方法的呼叫者。 OnClosing()、OnClosed() 和 OnAbort() 的實作不應封鎖(例如,在輸入/輸出上)。 下圖更詳細地顯示中止程序。

ICommunicationObject.Abort 狀態變更的數據流圖表。
覆寫 OnAbort 方法以實作自訂終止邏輯,例如終止內部通訊物件。

故障

Fault 方法是特定於 CommunicationObject 的,並不屬於 ICommunicationObject 介面的部分。 此處涵蓋此內容以確保完整。

前置條件:無。

後置條件:狀態發生錯誤。 可能會拋出例外狀況。

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

ThrowIfXxx 方法

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

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

ThrowIfDisposedOrImmutable 如果未建立狀態,則會擲回例外狀況。

ThrowIfDisposedOrNotOpen 如果狀態未開啟,則會拋出例外狀況。

拋出的例外取決於狀態。 下表顯示呼叫 ThrowIfXxx 時擲回該狀態的不同狀態及對應的例外狀況類型。

是否已執行中止? 例外狀況
已建立 N/A System.InvalidOperationException
開幕 N/A System.InvalidOperationException
已開啟 N/A System.InvalidOperationException
結束 是的 System.ServiceModel.CommunicationObjectAbortedException
結束 System.ObjectDisposedException
已關閉 是的 System.ServiceModel.CommunicationObjectAbortedException 如果物件是由先前明確呼叫 Abort 所關閉,則這種情況會發生。 如果您在物件上呼叫 Close,則會 System.ObjectDisposedException 擲回 。
已關閉 System.ObjectDisposedException
發生錯誤 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(預設通訊超時設定介面)

此介面有四個只讀屬性,可提供開啟、傳送、接收和關閉的預設逾時值。 每個實作都會負責以任何適當的方式取得預設值。 為了方便起見,ChannelFactoryBaseChannelListenerBase 會將這些值預設為 1 分鐘。