リモート処理の例 : チャネル シンク プロバイダ
チャネル シンク プロバイダの ChannelSinkPropertySetterProvider
を構築するコード例を次に示します。このチャネル シンク プロバイダは、アプリケーション構成ファイルの値を読み込み、シンク スタックをウォークして、構成ファイルに指定されているプロパティをサポートするチャネル シンクを検索できます。これは、プログラムによってシンク チェーンを作成し、コンストラクタに IDictionary オブジェクトを渡すのではなく、構成ファイルを使用してシンク チェーンを構築する場合に役立つ方法です。
このプロバイダは、チャネル シンク チェーンにシンクを挿入しません。このプロバイダは、単にシンク チェーンをウォークして互換性のあるプロパティを検索するだけです。また、特定のシンクでサポートされるプロパティだけを取得するように、簡単にカスタマイズできます。
なお、インフラストラクチャの他の部分が C# プログラミング言語で書かれているのに対し、このサンプル プロバイダは Visual Basic でコーディングおよびコンパイルされています。このプロバイダの C# バージョンも含まれていますが、示されたコマンド ラインではコンパイルしていません。
ヒント
.NET リモート処理では、既定では認証または暗号化を行いません。したがって、クライアントやサーバーとリモートで対話する前に、それらの ID の確認に必要な手順をすべて実行することをお勧めします。.NET リモート処理アプリケーションの実行には、FullTrust アクセス許可が必要です。そのため、承認されていないクライアントがサーバー上でのアクセスを許可された場合は、完全な信頼を与えられているものとして、コードを実行できてしまいます。インターネット インフォメーション サービス (IIS : Internet Information Services) でリモート型をホストするか、リモート型をホストするためのカスタム チャネル シンク ペアを構築することによって、常にエンドポイントを認証し、通信ストリームを暗号化してください。
このサンプルをコンパイルして実行するには
コマンド プロンプトで次のコマンドを入力します。
vbc -r:System.Runtime.Remoting.dll -t:library /out:PropsSink.dll ChannelSinkPropertySetterProvider.vb
csc /r:System.Runtime.Remoting.dll /t:library /out:ServiceClass.dll serviceclass.cs
csc /r:System.Runtime.Remoting.dll /r:ServiceClass.dll /r:PropsSink.dll client.cs
csc /r:System.Runtime.Remoting.dll /r:ServiceClass.dll server.cs
同じディレクトリを指す 2 つのコマンド プロンプトを開きます。一方のコマンド プロンプトでは、「server」と入力します。実行が開始されたら、もう一方のコマンド プロンプトで「client」と入力します。
このチャネル シンク プロバイダは、プロバイダのアクティビティのコンソール ダンプをクライアント コンソールに表示するかどうかを示す writeToConsole 属性をサポートしています。この例では、この属性は true に設定されています。
このアプリケーションは、1 台のコンピュータ上で、またはネットワーク経由で実行されます。このアプリケーションをネットワーク経由で実行するには、クライアント構成の "localhost" をリモート コンピュータの名前で置き換えます。
ChannelSinkPropertySetterProvider.vb
Imports System
Imports System.Collections
Imports System.IO
Imports System.Reflection
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
Imports System.Runtime.Remoting.Messaging
Imports System.Runtime.Remoting.MetadataServices
' The following is required only for command-line compilation.
' In the IDE it is implicit.
Imports Microsoft.VisualBasic
' This class implements a client-side channel sink provider that
' walks the channel sink chain, looking for channel sink
' properties that match those specified in the configuration file. If it
' finds them, the provider sets them to the values specified in the
' configuration file. This is a simple helper provider that returns no
' channel itself. Instead, it merely returns the next channel sink it can ' find, or Nothing.
Public Class ChannelSinkPropertySetterProvider
Implements IClientChannelSinkProvider
Private _next As IClientChannelSinkProvider = Nothing
Private _channelSinkProperties As IDictionary = Nothing
Private _providerData As ICollection = Nothing
' Sets the writeToConsole attribute on this provider element in the
' configuration file to "True"; otherwise, information is not written to the console.
Private _consoleDump As Boolean = False
' Default constructor.
Public Sub New()
Console.WriteLine("Default constructor called.")
End Sub 'New
' ChannelSinkPropertySetterProvider.
' Constructor with properties. If writeToConsole attribute is "True",
' this constructor will dump all custom configuration properties set in
' the configuration file.
Public Sub New(properties As IDictionary, providerData As ICollection)
_channelSinkProperties = properties
' Sets the private console dump property for this provider.
If Not (properties("writeToConsole") Is Nothing) Then
_consoleDump = [Boolean].Parse(properties("writeToConsole").ToString())
End If
_providerData = providerData
If _consoleDump Then
Console.WriteLine("ChannelSinkPropertySetterProvider custom constructor called.")
Dim sinkData As SinkProviderData
For Each sinkData In providerData
Console.WriteLine("SinkProvider element: " & sinkData.Name)
Dim prop As DictionaryEntry
For Each prop In sinkData.Properties
Console.WriteLine("Prop name: " & prop.Key.ToString() & " value: " & prop.Value.ToString())
Next prop
Dim child As Object
For Each child In sinkData.Children
Console.WriteLine("Child: " & child.GetType().Name)
Next child
Next sinkData
Dim entry As DictionaryEntry
For Each entry In properties
Console.WriteLine("channel sink properties: " & entry.Key.ToString() & ", " & entry.Value.ToString())
Next entry
Console.WriteLine()
End If
End Sub 'New
' ChannelSinkPropertySetterProvider.
' Called by the channel. Normally, this method takes any other sinks
' created by other providers in the chain, links them together, and
' then returns its own sink to the channel. In this case, this provider
' merely sets matching properties on each channel sink in the chain,
' and then returns the **next** channel sink to the channel or returns
' null, indicating to the channel that it is the end of the custom
' channel sink chain.
Public Function CreateSink(ByVal channel As IChannelSender, ByVal url As String, ByVal remoteChannelData As Object) As IClientChannelSink Implements IClientChannelSinkProvider.CreateSink
If _consoleDump Then
Console.WriteLine("CreateSink is called.")
' To invoke GetType on an interface, cast the
' interface to type Object, using CType.
Console.WriteLine("By " & CType(channel, Object).GetType.Name)
End If
Dim nextSink As IClientChannelSink = Nothing
If Not (_next Is Nothing) Then
nextSink = _next.CreateSink(channel, url, remoteChannelData)
If nextSink Is Nothing Then
If _consoleDump Then
Console.WriteLine("Next sink is null!")
End If
Return Nothing
End If
WalkSinkChain(nextSink)
End If
Return nextSink
End Function 'CreateSink
' This call walks the sink chain, setting properties as it goes.
' The channelSinkProperties are the SinkProviderData dictionaries
' that contain the name of the subnode in the configuration file, and
' a dictionary entry of attribute/value entries on that element.
Private Sub WalkSinkChain(ByVal thisSink As IClientChannelSink)
If thisSink Is Nothing Then
Return
End If
If _consoleDump Then
Console.WriteLine(ControlChars.CrLf & ControlChars.Tab & "Walking the sink chain to find sink properties... " & ControlChars.CrLf)
End If
While Not (thisSink Is Nothing)
If _consoleDump Then
Console.WriteLine(New [String]("_"c, 80))
' To invoke GetType on an interface, cast the
' interface to type Object, using CType.
Console.WriteLine("Next sink is : " & CType(thisSink, Object).GetType().Name)
DumpSinkProperties(thisSink)
End If
SetSinkProperties(thisSink)
thisSink = thisSink.NextChannelSink
End While
Return
End Sub 'WalkSinkChain
Private Sub DumpSinkProperties(ByVal sink As IClientChannelSink)
If sink.Properties Is Nothing Then
' To invoke GetType on an interface, cast the
' interface to type Object, using CType.
Console.WriteLine("There are no properties available on the " & CType(sink, Object).GetType().Name & " channelsink.")
Return
End If
Dim entry As DictionaryEntry
For Each entry In sink.Properties
Console.Write("ChannelSink property: " & entry.Key.ToString() & " value: ")
If entry.Value Is Nothing Then
Console.WriteLine("No value.")
Else
Console.WriteLine(entry.Value.ToString())
End If
Next entry
End Sub 'DumpSinkProperties
' This method sets properties on the sink. The algorithm is
' that in the absence of instance attribute/value entries, the
' provider element template attribute/value entries will be set.
' This is a simple implementation that does not care about the
' element name underneath the provider element.
Private Sub SetSinkProperties(ByVal sink As IClientChannelSink)
If sink.Properties Is Nothing Then
' To invoke GetType on an interface, cast the
' interface to type Object, using CType.
Console.WriteLine("There are no properties available on the " & CType(sink, Object).GetType().Name & " channelsink.")
Return
End If
Dim entry As DictionaryEntry
For Each entry In sink.Properties
If _channelSinkProperties.Contains(entry.Key) Then
If _consoleDump Then
' To invoke GetType on an interface, cast
' the interface to type Object, using CType.
Console.WriteLine("Setting sink property template on " & CType(sink, Object).GetType().Name & "." & entry.Key.ToString())
End If
sink.Properties(entry.Key) = _channelSinkProperties(entry.Key)
End If
Next entry
Dim provider As SinkProviderData
For Each provider In _providerData
Dim configEntry As DictionaryEntry
For Each configEntry In provider.Properties
If sink.Properties.Contains(configEntry.Key) Then
If _consoleDump Then
' To invoke GetType on an interface,
' cast the interface to type Object, using CType.
Console.WriteLine("Setting Instance override on " & CType(sink, Object).GetType().Name & "." & configEntry.Key)
End If
End If
sink.Properties(configEntry.Key) = configEntry.Value
Next configEntry
Next provider
If _consoleDump Then
DumpSinkProperties(sink)
End If
End Sub 'SetSinkProperties
' "Next" is a Visual Basic keyword; to use it as a
' property name you must place it in square brackets.
Public Property [Next]() As IClientChannelSinkProvider Implements IClientChannelSinkProvider.Next
Get
Return _next
End Get
Set(ByVal Value As IClientChannelSinkProvider)
_next = Value
End Set
End Property
' This can be called in the constructor in case this provider is
' intended to build its own channel sink provider chain. Without
' providing such a chain, this provider must be specified in a
' configuration file with other providers.
Private Function CreateDefaultClientProviderChain() As IClientChannelSinkProvider
Dim chain = New SoapClientFormatterSinkProvider()
Dim sink As IClientChannelSinkProvider = chain
sink.Next = New BinaryClientFormatterSinkProvider()
sink = sink.Next
Return chain
End Function 'CreateDefaultClientProviderChain ' CreateDefaultClientProviderChain
End Class 'ChannelSinkPropertySetterProvider
' Class ChannelSinkPropertySetterProvider.
ChannelSinkPropertySetterProvider.cs
このファイルは、情報の提供だけを目的としています。
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.MetadataServices;
// This class implements a client-side channel sink provider that
// walks the channel sink chain, looking for channel sink
// properties that match those specified in the configuration file. If it
// finds them, the provider sets them to the values specified in the
// configuration file. This is a simple helper provider that returns no
// channel itself. Instead, it merely returns the next channel sink it can // find, or null.
public class ChannelSinkPropertySetterProvider : IClientChannelSinkProvider{
private IClientChannelSinkProvider _next = null;
private IDictionary _channelSinkProperties = null;
private ICollection _providerData = null;
// Set the writeToConsole attribute on this provider element in the
// configuration file to "true"; otherwise, information is not written to the console.
private bool _consoleDump = false;
// Default constructor.
public ChannelSinkPropertySetterProvider(){
Console.WriteLine("Default constructor called.");
} // ChannelSinkPropertySetterProvider.
// Constructor with properties. If writeToConsole attribute is "true",
// this constructor will dump all custom configuration properties set
// in the configuration file.
public ChannelSinkPropertySetterProvider(IDictionary properties, ICollection providerData){
_channelSinkProperties = properties;
// Sets the private console dump property for this provider.
if (properties["writeToConsole"] != null)
_consoleDump = Boolean.Parse(properties["writeToConsole"].ToString());
_providerData = providerData;
if (_consoleDump){
Console.WriteLine("ChannelSinkPropertySetterProvider custom constructor called.");
foreach(SinkProviderData sinkData in providerData){
Console.WriteLine("SinkProvider element: " + sinkData.Name);
foreach(DictionaryEntry prop in sinkData.Properties){
Console.WriteLine("Prop name: " + prop.Key.ToString() + " value: " + prop.Value.ToString());
}
foreach(object child in sinkData.Children){
Console.WriteLine("Child: " + child.GetType().Name);
}
}
foreach (DictionaryEntry entry in properties){
Console.WriteLine("channel sink properties: " + entry.Key.ToString() + ", " + entry.Value.ToString());
}
Console.WriteLine();
}
} // ChannelSinkPropertySetterProvider.
// Called by the channel. Normally, this method takes any other sinks
// created by other providers in the chain, links them together, and
// then returns its own sink to the channel. In this case, this
// provider merely sets matching properties on each channel sink in the
// chain, and then returns the **next** channel sink to the channel or
// returns null, indicating to the channel that it is the end of the
// custom channel sink chain.
public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData){
if (_consoleDump){
Console.WriteLine("CreateSink is called.");
Console.WriteLine("By " + channel.GetType().Name);
}
IClientChannelSink nextSink = null;
if (_next != null){
nextSink = _next.CreateSink(channel, url, remoteChannelData);
if (nextSink == null){
if (_consoleDump)
Console.WriteLine("Next sink is null!");
return null;
}
WalkSinkChain(nextSink);
}
return nextSink;
}
// This call walks the sink chain, setting properties as it goes.
// The channelSinkProperties are the SinkProviderData dictionaries
// that contain the name of the subnode in the configuration file, and
// a dictionary entry of attribute/value entries on that element.
private void WalkSinkChain(IClientChannelSink thisSink){
if (thisSink == null)
return;
if (_consoleDump)
Console.WriteLine("\r\n\tWalking the sink chain to find sink properties... \r\n");
while(thisSink != null){
if (_consoleDump){
Console.WriteLine(new String('_',80));
Console.WriteLine("Next sink is : " + thisSink.GetType().Name);
DumpSinkProperties(thisSink);
}
SetSinkProperties(thisSink);
thisSink = thisSink.NextChannelSink;
}
return;
}
private void DumpSinkProperties(IClientChannelSink sink){
if (sink.Properties == null){
Console.WriteLine("There are no properties available on the " + sink.GetType().Name + " channelsink.");
return;
}
foreach(DictionaryEntry entry in sink.Properties){
Console.Write("ChannelSink property: " + entry.Key.ToString() + " value: ");
if (entry.Value == null)
Console.WriteLine("No value.");
else
Console.WriteLine(entry.Value.ToString());
}
}
// This method sets properties on the sink.
// The algorithm is that in the absence of instance attribute/value
// entries, the provider element template attribute/value entries will
// be set. This is a simple implementation that does not care about the
// element name underneath the provider element.
private void SetSinkProperties(IClientChannelSink sink){
if (sink.Properties == null){
Console.WriteLine("There are no properties available on the " + sink.GetType().Name + " channelsink.");
return;
}
foreach(DictionaryEntry entry in sink.Properties){
if (_channelSinkProperties.Contains(entry.Key)){
if (_consoleDump)
Console.WriteLine("Setting sink property template on " + sink.GetType().Name + "." + entry.Key.ToString());
sink.Properties[entry.Key] = _channelSinkProperties[entry.Key];
}
}
foreach(SinkProviderData provider in _providerData){
foreach(DictionaryEntry configEntry in provider.Properties){
if (sink.Properties.Contains(configEntry.Key))
if (_consoleDump)
Console.WriteLine("Setting Instance override on " + sink.GetType().Name + "." + configEntry.Key);
sink.Properties[configEntry.Key] = configEntry.Value;
}
}
if (_consoleDump)
DumpSinkProperties(sink);
}
public IClientChannelSinkProvider Next{
get {
return _next;
}
set {
_next = value;
}
}
// This can be called in the constructor in case this provider is
// intended to build its own channel sink provider chain. Without
// providing such a chain, this provider must be specified in a
// configuration file with other providers.
private IClientChannelSinkProvider CreateDefaultClientProviderChain(){
IClientChannelSinkProvider chain = new SoapClientFormatterSinkProvider();
IClientChannelSinkProvider sink = chain;
sink.Next = new BinaryClientFormatterSinkProvider();
sink = sink.Next;
return chain;
} // CreateDefaultClientProviderChain.
} // Class ChannelSinkPropertySetterProvider.
Client.cs
using System;
using System.Runtime.Remoting;
public class Client{
public static void Main(string[] Args){
RemotingConfiguration.Configure("Client.exe.config");
ServiceClass service = new ServiceClass();
Console.WriteLine(service.GetServerTime());
}
}
Server.cs
using System;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
public class ServerProcess{
public static void Main(string[] Args){
RemotingConfiguration.Configure("server.exe.config");
Console.WriteLine("Press enter to stop this process.");
Console.ReadLine();
}
}
ServiceClass.cs
using System;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
public class ServiceClass : MarshalByRefObject{
private DateTime starttime;
public ServiceClass(){
Console.WriteLine("A ServiceClass has been created.");
starttime = DateTime.Now;
}
~ServiceClass(){
Console.WriteLine("This object is being collected after " + (new TimeSpan(DateTime.Now.Ticks - starttime.Ticks)).ToString() + " seconds.");
}
public DateTime GetServerTime(){
Console.WriteLine("Time requested by a client.");
return DateTime.Now;
}
}
Client.exe.config
<configuration>
<system.runtime.remoting>
<application>
<client>
<wellknown
type="ServiceClass, ServiceClass"
url="https://localhost:8080/RemoteObject"
/>
</client>
<channels>
<channel ref="http">
<clientProviders>
<formatter ref="soap"/>
<provider ref="propsetter" username="bob" writeToConsole="true">
<endpoint allowAutoRedirect="true"/>
<endpoint preauthenticate="true"/>
<endpoint url="example.com:9000" password="xyz" />
<endpoint url="example.com:9001" password="123" />
<endpoint timeout="10000"/>
<endpoint url="example.com:*" username="bob2" password="qwerty" domain="hello" />
</provider>
</clientProviders>
</channel>
</channels>
</application>
<channelSinkProviders>
<clientProviders>
<provider
id="propsetter"
type="ChannelSinkPropertySetterProvider, PropsSink"
/>
</clientProviders>
</channelSinkProviders>
<debug loadTypes="true" />
</system.runtime.remoting>
</configuration>
Server.exe.config
<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown mode="SingleCall"
type="ServiceClass, ServiceClass"
objectUri="RemoteObject"
/>
</service>
<channels>
<channel port="8080" ref="http" />
</channels>
</application>
</system.runtime.remoting>
</configuration>