シンクとシンク チェーン
チャネルは、メッセージを送信する前、またはメッセージを受信した後に、チャネル シンク オブジェクトのチェーンに各メッセージを送信します。このシンク チェーンには、チャネルの基本的機能に必要な、フォーマッタ シンク、トランスポート シンク、スタックビルダ シンクなどのシンクが含まれていますが、チャネル シンク チェーンをカスタマイズして、メッセージやストリームに対して特別なタスクを実行できます。
各チャネル シンクは、IClientChannelSink または IServerChannelSink を実装します。クライアント側の最初のチャネル シンクは、IMessageSink も実装する必要があります。このシンクは通常、IMessageSink、IChannelSinkBase、および IClientChannelSink から継承する IClientFormatterSink を実装し、着信メッセージをストリーム (IMessage オブジェクト) に変換するため、フォーマッタ シンクと呼ばれます。
チャネル シンク チェ-ンは、アプリケーション ドメインに対して送信された、またはアプリケーション ドメインから送信されたすべてのメッセージを処理します。この時点ではメッセージを取得しただけですが、このメッセージに対して任意の操作を行うことができます。以降の処理では、操作後にシステムに返されたメッセージが使用されます。チャネル シンク チェーンは、クライアントまたはサーバーでログ サービス、任意の種類のフィルタ、暗号化、その他のセキュリティ機能を実装するために適した場所です。基本的なチャネル シンク チェーンの構造を次の図に示します。
基本的なチャネル シンク チェーン
各チャネル シンクはストリームを処理し、そのストリームを次のチャネル シンクに渡すため、シンクの前後にあるオブジェクトは、渡されたストリームの処理方法について情報を持っている必要があります。
メモ : |
---|
メッセージ シンクは、例外をスローできません。メッセージ シンクがこれを制御する 1 つの方法は、try ブロックから catch ブロックまでのメソッド コードをラップすることです。 |
リモート処理メッセージを転送するチャネル シンクは、チャネル シンク プロバイダ (IClientChannelSinkProvider、IClientFormatterSinkProvider、または IServerChannelSinkProvider の各インターフェイスを実装するオブジェクト) が作成します。リモート型がアクティブ化されると、チャネルからチャネル シンク プロバイダが取得され、シンク プロバイダ上で CreateSink メソッドが呼び出され、チェーンから最初のチャネル シンクが取得されます。
チャネル シンクは、クライアントとサーバーとの間でメッセージを転送します。また、チャネル シンクは、チェーン内で相互にリンクされています。シンク プロバイダの CreateSink メソッドは、呼び出されたときに次のことを行う必要があります。
専用のチャネル シンクを作成します。
チェーン内の次のシンク プロバイダ上で CreateSink を呼び出します。
次のシンクと現在のシンクが相互にリンクされるようにします。
シンクを呼び出し元に返します。
チャネル シンクは、そこで行われたすべての呼び出しをチェーン内の次のシンクに転送します。また、次のシンクへの参照を格納するための機構を提供する必要があります。
チャネル シンクには、シンク チェーン内で転送する内容について、高い柔軟性があります。たとえば、シリアル化された元のメッセージを送信する前に認証をネゴシエートする必要のあるセキュリティ シンクは、完全なチャネル メッセージを保持し、コンテンツ ストリームをシンク自身の内容で置き換え、シンク チェーンを通じてリモートのアプリケーション ドメインまで送信できます。逆方向の処理を行う場合には、セキュリティ シンクは、リモートのアプリケーション ドメインの対応するセキュリティ シンクとの間で通信を確立して、応答メッセージを受け取ることができます。合意に到達すると、送信元のセキュリティ シンクは元のコンテンツ ストリームをリモートのアプリケーション ドメインに送信できます。
チャネル シンク チェーンでのメッセージ処理
.NET リモート処理システムは、IMethodCallMessage 実装を処理できるチャネルを見つけると、このチャネルは、IMessageSink.SyncProcessMessage (または IMessageSink.AsyncProcessMessage) を呼び出して、メッセージをフォーマッタ チャネル シンクに渡します。フォーマッタ シンクはトランスポート ヘッダー配列を作成し、次のシンクの IClientChannelSink.GetRequestStream を呼び出します。この呼び出しはシンク チェーン内を転送されます。また、どのシンクもフォーマッタ シンクに返送される要求ストリームを作成できます。GetRequestStream が null 参照 (Visual Basic では Nothing) を返した場合、フォーマッタ シンクはシリアル化に使用する独自のシンクを作成します。この呼び出しが返されると、メッセージはシリアル化され、シンク チェーンの最初のチャネル シンクで適切なメッセージ処理メソッドが呼び出されます。
シンクは、ストリームにデータを書き込むことはできませんが、ストリームからデータを読み込んだり、必要な場合には新しいストリームをシンクどうしで渡したりできます。シンクは、ヘッダー配列にヘッダーを追加したり (これまでに次のシンクの GetRequestStream を呼び出していない場合)、呼び出しを次のシンクに転送する前に自分自身をシンク スタックに追加したりできます。呼び出しがチェーンの最後にあるトランスポート シンクに到達すると、トランスポート シンクはヘッダーおよびシリアル化されたメッセージをチャネル経由でサーバーに送信し、サーバーでプロセス全体が逆方向に行われます。サーバー側のトランスポート シンクは、ヘッダーおよびシリアル化されたメッセージをストリームのサーバー側から取得し、シンク チェーンを通じてフォーマッタ シンクまで転送します。フォーマッタ シンクはメッセージを逆シリアル化してリモート処理システムに転送します。リモート処理システムでは、メッセージをメソッド呼び出しに戻し、サーバー オブジェクト上で呼び出します。
チャネル シンク チェーンの作成
新しいチャネル シンクを作成するには、リモート処理システムを実装および構成して、IServerChannelSinkProvider または IClientChannelSinkProvider の実装を認識するようにします。これにより、カスタム IClientChannelSink やカスタム IServerChannelSink の実装を作成したり、チェーン内の次のシンクを検索したりできます。カスタム チャネル シンクを実装する場合は、BaseChannelSinkWithProperties 抽象クラスを使用すると役立ちます。
チャネル シンク プロバイダの構築
アプリケーションは、パラメータとして、サーバー チャネル シンク プロバイダまたはクライアント チャネル シンク プロバイダをチャネルの構築時に提供できます。チャネル シンク プロバイダはチェーン内に格納する必要があり、ユーザーは、外側のチャネル シンク プロバイダがチャネル コンストラクタに渡される前に、すべてのチャネル シンク プロバイダを相互にチェーンしておく必要があります。チャネル シンク プロバイダは、この目的のために Next プロパティを実装します。クライアント側チャネル シンク プロバイダを構築する方法を、次のコード例に示します。完全なコード例については、「リモート処理の例 : チャネル シンク プロバイダ」を参照してください。
private Function CreateDefaultClientProviderChain() As IClientChannelSinkProvider
Dim chain As New FirstClientFormatterSinkProvider
Dim sink As IClientChannelSinkProvider
sink = chain
sink.Next = New SecondClientFormatterSinkProvider
sink = sink.Next
return chain
End Function
private IClientChannelSinkProvider CreateDefaultClientProviderChain(){
IClientChannelSinkProvider chain = new FirstClientFormatterSinkProvider();
IClientChannelSinkProvider sink = chain;
sink.Next = new SecondClientFormatterSinkProvider();
sink = sink.Next;
return chain;
}
メモ : |
---|
構成ファイルに複数のチャネル シンク プロバイダを記述すると、リモート処理システムはそれらのチャネル シンク プロバイダを記述されている順にチェーンします。チャネル シンク プロバイダは、RemotingConfiguration.Configure 呼び出し中にチャネルが作成されるときに生成されます。 |
フォーマッタ シンク
フォーマッタ シンクは、チャネル メッセージをシリアル化し、IMessage を実装するオブジェクトとしてメッセージ ストリームに変換します。一部のフォーマッタ シンクの実装では、システムが提供するフォーマッタ型 (BinaryFormatter および SoapFormatter) を使用します。その他の実装では、独自の方法を使用してチャネル メッセージをストリームに変換します。
フォーマッタ シンクは、必要なヘッダーを作成し、メッセージをシリアル化してストリームに変換する機能を備えています。フォーマッタ シンクの後、メッセージは IMessageSink.ProcessMessage または Imessagesink.AsyncProcessMessage 呼び出し経由で、シンク チェーン内のすべてのシンクに転送されます。メッセージはこの段階で既にシリアル化されていて、情報だけ提供されます。
メモ : |
---|
メッセージを作成または変更する必要のあるシンクは、シンク チェーンでフォーマッタの前に配置する必要があります。これは、IClientFormatterSink を実装し、システムに対してフォーマッタ シンクへの参照が存在するかのように偽装することで、簡単に実現できます。そのうえで、本当のフォーマッタ シンクをシンク チェーン内の後続の位置に配置します。 |
逆方向の処理を行うときには、フォーマッタ シンクはメッセージ ストリームをチャネル メッセージ要素 (戻りメッセージ) に戻します。クライアント側の最初のシンクは、IClientFormatterSink インターフェイスを実装する必要があります。CreateSink がチャネルに返されると、返された参照は IMessage インターフェイスの SyncProcessMessage を呼び出せるように、IClientFormatterSink 型にキャストされます。キャストに失敗すると、システムは例外を発生させます。
カスタム チャネル シンク
クライアント側では、フォーマッタ シンクと最後のトランスポート シンクとの間にあるオブジェクトのチェーンに、カスタム チャネル シンクが挿入されます。クライアントまたはサーバーのチャネルにカスタム チャネル シンクを挿入することにより、次のいずれかの時点で IMessage を処理できます。
メッセージとして表された呼び出しをストリームに変換し、ネットワーク経由で送信する処理の間。
ストリームがネットワークから取得され、StackBuilderSink オブジェクト (サーバーのリモート オブジェクトの前にある最後のメッセージ シンク) またはプロキシ オブジェクト (クライアント側) に送信される処理の間。
カスタム シンクはストリームに対してデータを (呼び出しが送信か着信に応じて) 読み書きでき、必要な場合にはヘッダーに情報を追加できます。メッセージはこの段階でフォーマッタによって既にシリアル化され、変更はできません。メッセージ呼び出しがチェーンの最後にあるトランスポート シンクに転送されると、トランスポート シンクはストリームにヘッダーを書き込み、チャネルで指定されたトランスポート プロトコルを使用して、ストリームをサーバー上のトランスポート シンクに転送します。
トランスポート シンク
トランスポート シンクは、クライアント側ではチェーンの最後にあり、サーバー側ではチェーンの最初にあるシンクです。トランスポート シンクは、シリアル化したメッセージを転送するだけでなく、サーバーにヘッダーを送信したり、サーバーから呼び出しが返されたときにヘッダーやストリームを取得したりします。これらのシンクはチャネルに組み込まれ、拡張はできません。
既定のフォーマッタの交換
チャネルは抽象的なネットワーク機構なので、.NET リモート処理システムを構成して、システムに実装されたチャネルと選択した任意のフォーマッタを結合できます。これを行うには、チャネルのプロパティの IDictionary 実装、サーバー側のフォーマッタ、およびクライアント側のフォーマッタを取るチャネル コンストラクタを使用します。フォーマッタは構成ファイルで指定することもできます。.NET リモート処理構成システムに対して、クライアント側で BinaryClientFormatterSink を使用して HttpChannel を作成するように指示する例を次に示します。
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http">
<clientProviders>
<formatter ref="binary"/>
</clientProviders>
<channels>
</application>
</system.runtime.remoting>
</configuration>
上のコードと同じ処理をプログラムで実行するコードを次に示します。このコードでは、GetServerString と GetServerTime を実装したリモート インターフェイス型の IService が存在することを想定しています。
Imports System
Imports System.Collections
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
Public Class ClientProcess
<MTAThread()> _
Public Shared Sub Main()
' Note that any name/value pairs of configuration attributes can be
' placed in this dictionary (the configuration system calls this same
' constructor).
Dim properties As New Hashtable()
properties("name") = "HttpBinary"
ChannelServices.RegisterChannel(New HttpChannel(properties, New BinaryClientFormatterSinkProvider(), Nothing))
' The last parameter above (Nothing) is the server sink provider chain
' to obtain the default behavior (which includes SOAP and
' binary formatters on the server side).
Dim service As IService = CType(Activator.GetObject(GetType(IService), "http://computer:8080/SAService"), IService)
Console.WriteLine("Server string is: " + service.GetServerString())
Console.WriteLine("Server time is: " + service.GetServerTime())
End Sub
End Class
using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
public class ClientProcess{
public static void Main(string[] Args){
// Note that any name/value pairs of configuration attributes can be
// placed in this dictionary (the configuration system calls this
// same HttpChannel constructor).
IDictionary properties = new Hashtable();
properties["name"] = "HttpBinary";
// The last parameter below is the server sink provider chain
// to obtain the default behavior (which includes SOAP and binary
// formatters) on the server side.
ChannelServices.RegisterChannel(new HttpChannel(null, new BinaryClientFormatterSinkProvider(), null));
IService service = (IService)Activator.GetObject(typeof(IService),"http://computer:8080/SAService");
Console.WriteLine("Server string is: " + service.GetServerString());
Console.WriteLine("Server time is: " + service.GetServerTime());
}
}
上に示した、インターネット インフォメーション サービス (IIS : Internet Information Services) でホストされるチャネルとフォーマッタの組み合わせの完全な例については、「リモート処理の例 : インターネット インフォメーション サービス (IIS) でのホスト」を参照してください。
上の例のクライアントが、SoapClientFormatterSink オブジェクトで TcpChannel オブジェクトを使用するように変更するには、次のコード例に示すように、名前空間と RegisterChannel の呼び出しを変更します。
ChannelServices.RegisterChannel(New TcpChannel(properties, New SoapClientFormatterSinkProvider(), Nothing))
ChannelServices.RegisterChannel(new TcpChannel(null, new SoapClientFormatterSinkProvider(), null));
参照
概念
インターネット インフォメーション サービス (IIS) でのリモート オブジェクトのホスト
リモート処理の例 : インターネット インフォメーション サービス (IIS) でのホスト