シンクとシンク チェイン

チャネルは、メッセージを送信する前、またはメッセージを受信した後に、チャネル シンク オブジェクトのチェインに各メッセージを送信します。このシンク チェインには、チャネルの基本的機能に必要な、フォーマッタ シンク、トランスポート シンク、スタックビルダ シンクなどのシンクが含まれていますが、チャネル シンク チェインをカスタマイズして、メッセージやストリームに対して特別なタスクを実行できます。

各チャネル シンクは、IClientChannelSink または IServerChannelSink を実装します。クライアント側の最初のチャネル シンクは、IMessageSink も実装する必要があります。このシンクは通常、IMessageSinkIChannelSinkBase、および IClientChannelSink から継承する IClientFormatterSink を実装し、着信メッセージをストリーム (IMessage オブジェクト) に変換するため、フォーマッタ シンクと呼ばれます。

チャネル シンク チェインは、アプリケーション ドメインに対して送信された、またはアプリケーション ドメインから送信されたすべてのメッセージを処理します。この時点ではメッセージを取得しただけですが、このメッセージに対して任意の操作を行うことができます。以降の処理では、操作後にシステムに返されたメッセージが使用されます。チャネル シンク チェインはクライアントまたはサーバーでログ サービス、任意の種類のフィルタ、暗号化、その他のセキュリティ機能を実装するために適した場所です。基本的なチャネル シンク チェインの構造を次の図に示します。

基本的なチャネル シンク チェイン

各チャネル シンクはストリームを処理し、そのストリームを次のチャネル シンクに渡すため、シンクの前後にあるオブジェクトは、渡されたストリームの処理方法について情報を持っている必要があります。

メモ   メッセージ シンクは、例外をスローできません。メッセージ シンクがこれを制御する 1 つの方法は、try ブロックから catch ブロックまでのメソッド コードをラップすることです。

リモート処理メッセージを転送するチャネル シンクは、チャネル シンク プロバイダ (IClientChannelSinkProviderIClientFormatterSinkProvider、または IServerChannelSinkProvider の各インターフェイスを実装するオブジェクト) が生成します。リモート型がアクティブ化されると、チャネルからチャネル シンク プロバイダが取得され、シンク プロバイダ上で CreateSink メソッドが呼び出され、チェインからシンクの最初のチャネルが取得されます。

チャネル シンクは、クライアントとサーバーとの間でメッセージを転送します。また、チャネル シンクは、チェイン内で相互にリンクされています。シンク プロバイダの CreateSink メソッドは、呼び出されたときに次のことを行う必要があります。

  • 専用のチャネル シンクを生成します。
  • チェイン内の次のシンク プロバイダ上で CreateSink を呼び出します。
  • 次のシンクと現在のシンクが相互にリンクされるようにします。
  • シンクを呼び出し元に返します。

チャネル シンクは、そこで行われたすべての呼び出しをチェイン内の次のシンクに転送します。また、次のシンクへの参照を格納するための機構を提供する必要があります。

チャネル シンクには、シンク チェイン内で転送する内容について、高い柔軟性があります。たとえば、シリアル化された元のメッセージを送信する前に認証をネゴシエートする必要のあるセキュリティ シンクは、完全なチャネル メッセージを保持し、内容のストリームをシンク自身の内容で置き換え、シンク チェインを通じてリモートのアプリケーション ドメインまで送信できます。逆方向の処理を行う場合には、セキュリティ シンクは、リモートのアプリケーション ドメインの対応するセキュリティ シンクとの間で通信を確立して、応答メッセージを受け取ることができます。合意に到達すると、送信元のセキュリティ シンクは元の内容ストリームをリモートのアプリケーション ドメインに送信できます。

チャネル シンク チェインでのメッセージ処理

.NET リモート処理システムは、IMethodCallMessage 実装を処理できるチャネルを見つけると、このチャネルは、IMessageSink.SyncProcessMessage (または IMessageSink.AsyncProcessMessage) を呼び出して、メッセージをフォーマッタ チャネル シンクに渡します。フォーマッタ シンクはトランスポート ヘッダー配列を生成し、次のシンクの IClientChannelSink.GetRequestStream を呼び出します。この呼び出しはシンク チェイン内を転送されます。また、どのシンクもフォーマッタ シンクに返送される要求ストリームを生成できます。GetRequestStreamnull 参照 (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 
[C#]
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> 

上のコードと同じ処理をプログラムで実行するコードを次に示します。このコードでは、GetServerStringGetServerTime を実装したリモート インターフェイス型の 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 
[C#]
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, NewSoapClientFormatterSinkProvider(), Nothing))
[C#]
ChannelServices.RegisterChannel(new TcpChannel(null, new SoapClientFormatterSinkProvider(), null));

参照

高度なリモート処理 | インターネット インフォメーション サービス (IIS) でのリモート オブジェクトの管理 | リモート処理の例 : インターネット インフォメーション サービス (IIS) での管理