このトピックでは、チャネルに存在する状態と遷移、チャネルの状態を構成するために使用される型、およびそれらを実装する方法について説明します。
ステート マシンとチャネル
通常、ソケットなどの通信を処理するオブジェクトは、状態遷移がネットワーク リソースの割り当て、接続の作成または受け入れ、接続の終了、通信の終了に関連するステート マシンを示します。 チャネル ステート マシンは、そのオブジェクトの基になる実装を抽象化する通信オブジェクトの状態の統一モデルを提供します。 ICommunicationObject インターフェイスには、一連の状態、状態遷移メソッド、および状態遷移イベントが用意されています。 チャネル、チャネル ファクトリ、チャネル リスナーはすべて、チャネル ステート マシンを実装します。
イベント Closed、Closing、Faulted、Opened、Opening は、状態遷移が発生した後に外部オブザーバーに通知します。
メソッド Abort、Close、Open (および非同期の同等のメソッド) は、状態遷移を引き起こします。
state プロパティは、 CommunicationStateで定義されている現在の状態を返します。
ICommunicationObject、CommunicationObject、および状態と状態遷移
ICommunicationObjectは、さまざまなプロパティを構成できる [作成済み] 状態で開始されます。 Opened 状態になると、オブジェクトはメッセージの送受信に使用できますが、そのプロパティは不変と見なされます。 終了状態になると、オブジェクトは新しい送受信要求を処理できなくなりますが、既存の要求は Close タイムアウトに達するまで完了する可能性があります。 回復不可能なエラーが発生した場合、オブジェクトは Faulted 状態に遷移します。この状態では、エラーに関する情報を調べて最終的に閉じることができます。 Closed 状態の場合、オブジェクトは基本的にステート マシンの末尾に達しました。 ある状態から次の状態に遷移したオブジェクトは、前の状態に戻りません。
次の図は、 ICommunicationObject 状態と状態遷移を示しています。 状態遷移は、Abort、Open、または Close のいずれかのメソッドを呼び出すことによって発生する可能性があります。 また、他の実装固有のメソッドを呼び出すことによっても発生する可能性があります。 Faulted 状態への遷移は、通信オブジェクトを開いている間、または開いた後にエラーが発生した結果として発生する可能性があります。
すべての ICommunicationObject が作成済み状態で開始されます。 この状態では、アプリケーションはプロパティを設定してオブジェクトを構成できます。 オブジェクトが Created 以外の状態になると、変更不可と見なされます。
図 1. ICommunicationObject ステート マシン。
Windows Communication Foundation (WCF) には、ICommunicationObjectとチャネル ステート マシンを実装するCommunicationObjectという名前の抽象基本クラスが用意されています。 次の図は、 CommunicationObjectに固有の変更された状態図です。 ICommunicationObjectステート マシンに加えて、追加のCommunicationObject メソッドが呼び出されたときのタイミングも表示されます。
図 2. イベントと保護されたメソッドの呼び出しを含む ICommunicationObject ステート マシンの CommunicationObject 実装。
ICommunicationObject イベント
CommunicationObject は、 ICommunicationObjectによって定義された 5 つのイベントを公開します。 これらのイベントは、状態遷移の通知を受け取る通信オブジェクトを使用するコード用に設計されています。 上の図 2 に示すように、各イベントは、オブジェクトの状態がイベントによって指定された状態に遷移した後に 1 回発生します。 5 つのイベントはすべて、次のように定義されている EventHandler
の種類です。
public delegate void EventHandler(object sender, EventArgs e);
CommunicationObject実装では、送信者はCommunicationObject自体か、または送信者としてCommunicationObjectコンストラクターに渡されたもの (そのコンストラクターのオーバーロードが使用された場合) のいずれかです。 EventArgs パラメーター ( e
) は常に EventArgs.Empty
。
派生オブジェクトコールバック
5 つのイベントに加えて、 CommunicationObject は、状態遷移の前後に派生オブジェクトを呼び出せるように設計された 8 つの保護された仮想メソッドを宣言します。
CommunicationObject.OpenメソッドとCommunicationObject.Close メソッドには、それぞれ 3 つのコールバックが関連付けられています。 たとえば、CommunicationObject.OnOpening、CommunicationObject.OnOpen、CommunicationObject.OnOpenedがあるCommunicationObject.Openに対応します。 CommunicationObject.Closeに関連付けられているのは、CommunicationObject.OnClose、CommunicationObject.OnClosing、およびCommunicationObject.OnClosedメソッドです。
同様に、 CommunicationObject.Abort メソッドには対応する CommunicationObject.OnAbortがあります。
CommunicationObject.OnOpen、CommunicationObject.OnClose、およびCommunicationObject.OnAbortには既定の実装はありませんが、他のコールバックには、ステート マシンの正確性に必要な既定の実装があります。 これらのメソッドをオーバーライドする場合は、必ず基本実装を呼び出すか、正しく置き換えてください。
CommunicationObject.OnOpeningをCommunicationObject.OnClosingし、対応するCommunicationObject.Opening、CommunicationObject.Closing、およびCommunicationObject.FaultedイベントをCommunicationObject.OnFaultedします。 CommunicationObject.OnOpened と CommunicationObject.OnClosed オブジェクトの状態をそれぞれ Opened と Closed に設定し、対応する CommunicationObject.Opened イベントと CommunicationObject.Closed イベントを発生させます。
状態遷移メソッド
CommunicationObject は、Abort、Close、Open の実装を提供します。 また、状態が Faulted 状態に遷移する Fault メソッドも提供します。 図 2 は、その原因となるメソッドによってラベル付けされた各遷移を持つ ICommunicationObject ステート マシンを示しています (ラベル付けされていない遷移は、最後にラベル付けされた遷移を引き起こしたメソッドの実装内で発生します)。
注
通信状態取得/セットのすべての CommunicationObject 実装はスレッド同期されます。
コンストラクタ
CommunicationObject には 3 つのコンストラクターが用意されており、そのすべてがオブジェクトを Created 状態のままにします。 コンストラクターは次のように定義されます。
最初のコンストラクターは、オブジェクトを受け取るコンストラクター オーバーロードにデリゲートするパラメーターなしのコンストラクターです。
protected CommunicationObject() : this(new object()) { … }
オブジェクトを受け取るコンストラクターは、通信オブジェクトの状態へのアクセスを同期するときにロックされるオブジェクトとして、そのパラメーターを使用します。
protected CommunicationObject(object mutex) { … }
最後に、3 番目のコンストラクターは、 ICommunicationObject イベントが発生したときに sender 引数として使用される追加のパラメーターを受け取ります。
protected CommunicationObject(object mutex, object eventSender) { … }
前の 2 つのコンストラクターは、送信者をこれに設定します。
Open メソッド
前提条件: 状態が作成されます。
事後条件: 状態が開かれているか、障害が発生しています。 例外がスローされる場合があります。
Open() メソッドは、通信オブジェクトを開き、状態を Opened に設定しようとします。 エラーが発生した場合は、状態が Faulted に設定されます。
メソッドは、最初に現在の状態が Created であることを確認します。 現在の状態が [開いている] または [開いている] の場合は、 InvalidOperationExceptionがスローされます。 現在の状態が Closing または Closed の場合、オブジェクトが終了した場合は CommunicationObjectAbortedException がスローされ、それ以外の場合は ObjectDisposedException 。 現在の状態が Faulted の場合、 CommunicationObjectFaultedExceptionがスローされます。
次に、状態を Opening に設定し、その順序で OnOpening() (Opening イベントを発生させる)、OnOpen() および OnOpened() を呼び出します。 OnOpened() は状態を Opened に設定し、Opened イベントを発生させます。 これらのいずれかが例外をスローした場合、Open()は Fault() を呼び出し、例外をバブルアップさせます。 次の図は、Open プロセスの詳細を示しています。
内部通信オブジェクトを開くなどのカスタムオープン ロジックを実装するには、OnOpen メソッドをオーバーライドします。
Close メソッド
前提条件: なし。
事後条件: 状態はクローズです。 例外がスローされる場合があります。
Close() メソッドは、任意の状態で呼び出すことができます。 オブジェクトを正常に閉じようとします。 エラーが発生すると、オブジェクトが終了します。 現在の状態が Closing または Closed の場合、メソッドは何も行いません。 それ以外の場合は、状態が Closing に設定されます。 元の状態が作成、開いている、またはエラーが発生した場合は、Abort() を呼び出します (次の図を参照)。 元の状態が Opened の場合、その順序で OnClosing() (Closing イベントを発生させる)、OnClose() および OnClosed() を呼び出します。 これらのいずれかの例外がスローされた場合、Close()は Abort() を呼び出し、例外をバブルアップさせます。 OnClosed() は状態を Closed に設定し、Closed イベントを発生させます。 次の図は、Close プロセスの詳細を示しています。
内部通信オブジェクトを閉じるなど、カスタム閉じるロジックを実装するには、OnClose メソッドをオーバーライドします。 タイムアウト パラメーターを受け取り、Abort() の一部として呼び出されないため、長時間ブロックする可能性があるすべての正常終了ロジック (たとえば、もう一方の側が応答するのを待つ) を OnClose() に実装する必要があります。
アボート
前提条件: なし。
事後条件: 状態はクローズです。 例外がスローされる場合があります。
Abort() メソッドは、現在の状態が Closed の場合、またはオブジェクトが以前に終了された場合 (たとえば、Abort() が別のスレッドで実行されている場合など)、何も実行しません。 それ以外の場合は、状態を Closing に設定し、OnClosing() (Closing イベントを発生させる)、OnAbort()、OnClosed() をその順序で呼び出します (オブジェクトが終了しているため、OnClose は呼び出しません。閉じていません)。 OnClosed() は状態を Closed に設定し、Closed イベントを発生させます。 これらのいずれかの例外がスローされた場合は、Abort の呼び出し元に再スローされます。 OnClosing()、OnClosed()、OnAbort() の実装はブロックしないでください (入力/出力など)。 次の図は、中止プロセスの詳細を示しています。
OnAbort メソッドをオーバーライドして、内部通信オブジェクトの終了などのカスタム終了ロジックを実装します。
障害
Fault メソッドは CommunicationObject に固有であり、 ICommunicationObject インターフェイスの一部ではありません。 完全のためにここに含まれています。
前提条件: なし。
事後条件: 状態がエラーです。 例外がスローされる場合があります。
現在の状態が Faulted または Closed の場合、Fault() メソッドは何も実行しません。 それ以外の場合は、状態が Faulted に設定され、OnFaulted() が呼び出され、Faulted イベントが発生します。 OnFaulted が例外をスローした場合は、再スローされます。
ThrowIfXxx メソッド
CommunicationObject には、オブジェクトが特定の状態にある場合に例外をスローするために使用できる 3 つの保護されたメソッドがあります。
ThrowIfDisposed は、状態が Closing、Closed、または Faulted の場合に例外をスローします。
ThrowIfDisposedOrImmutable は、状態が作成されていない場合に例外をスローします。
ThrowIfDisposedOrNotOpen は、状態が Opened でない場合に例外をスローします。
スローされる例外は、状態によって異なります。 次の表は、さまざまな状態と、その状態でスローされる ThrowIfXxx を呼び出すことによってスローされる対応する例外の種類を示しています。
状態 | Abort が呼び出されましたか? | 例外 |
---|---|---|
作成 | なし | System.InvalidOperationException |
オープニング | なし | System.InvalidOperationException |
開始済み | なし | System.InvalidOperationException |
閉会 | イエス | System.ServiceModel.CommunicationObjectAbortedException |
閉会 | いいえ | System.ObjectDisposedException |
クローズ済みです | イエス | System.ServiceModel.CommunicationObjectAbortedException オブジェクトが Abort の以前の明示的な呼び出しによって閉じられた場合。 オブジェクトに対して Close を呼び出すと、 System.ObjectDisposedException がスローされます。 |
クローズ済みです | いいえ | System.ObjectDisposedException |
障害 | なし | System.ServiceModel.CommunicationObjectFaultedException |
タイムアウト
説明したいくつかのメソッドは、タイムアウト パラメーターを受け取ります。 これらは Close、Open (特定のオーバーロードと非同期バージョン)、OnClose、OnOpen です。 これらのメソッドは、長い操作 (たとえば、接続を正常に閉じながら入力/出力をブロックする) を可能にするように設計されているため、タイムアウト パラメーターは、中断されるまでにこのような操作にかかる時間を示します。 これらのメソッドの実装では、指定されたタイムアウト値を使用して、そのタイムアウト内に呼び出し元に確実に戻る必要があります。 タイムアウトを取らない他のメソッドの実装は、長い操作用に設計されていないため、入力/出力でブロックしないでください。
例外は、タイムアウトを取らない Open() および Close() オーバーロードです。 これらは、派生クラスによって提供される既定のタイムアウト値を使用します。 CommunicationObject は、 DefaultCloseTimeout という名前の 2 つの保護された抽象プロパティを公開し、次のように定義 DefaultOpenTimeout 。
protected abstract TimeSpan DefaultCloseTimeout { get; }
protected abstract TimeSpan DefaultOpenTimeout { get; }
派生クラスは、これらのプロパティを実装して、タイムアウト値を受け取らない Open() および Close() オーバーロードの既定のタイムアウトを提供します。 次に、Open() と Close() の実装は、既定のタイムアウト値を渡すタイムアウトを受け取るオーバーロードにデリゲートします。次に例を示します。
public void Open()
{
this.Open(this.DefaultOpenTimeout);
}
IDefaultCommunicationTimeouts
このインターフェイスには、開く、送信する、受信する、閉じるの既定のタイムアウト値を提供するための 4 つの読み取り専用プロパティがあります。 各実装は、適切な方法で既定値を取得する役割を担います。 便宜上、 ChannelFactoryBase と ChannelListenerBase の既定値は、それぞれ 1 分です。