リモート処理の例 : チャネル シンク プロバイダー
このトピックの対象は、既存のアプリケーションとの下位互換性のために残されているレガシ テクノロジに特定されています。新規の開発には、このトピックを適用しないでください。分散アプリケーションは、現在は Windows Communication Foundation (WCF) を使用して開発する必要があります。
チャネル シンク プロバイダーの ChannelSinkPropertySetterProvider
を構築するコード例を次に示します。このチャネル シンク プロバイダーは、アプリケーション構成ファイルの値を読み込み、シンク スタックをウォークして、構成ファイルに指定されているプロパティをサポートするチャネル シンクを検索できます。これは、プログラムによってシンク チェーンを作成し、コンストラクターに IDictionary オブジェクトを渡すのではなく、構成ファイルを使用してシンク チェーンを構築する場合に役立つ方法です。
このプロバイダーは、チャネル シンク チェーンにシンクを挿入しません。単にシンク チェーンをウォークしてプロパティ セットを検索するプロバイダーであり、特定のシンクでサポートされるプロパティだけを取得するように簡単にカスタマイズできます。
注意 : |
---|
.NET リモート処理は、既定では認証も暗号化も実行しません。したがって、クライアントやサーバーとリモートで通信する前に、それらの ID の確認に必要な手順をすべて実行することをお勧めします。.NET リモート処理アプリケーションの実行には、FullTrust アクセス許可が必要です。認証されていないクライアントがサーバーへのアクセスを許可された場合は、完全な信頼を与えられているものとして、コードを実行できてしまいます。インターネット インフォメーション サービス (IIS: Internet Information Services) でリモート型をホストするか、リモート型をホストするためのカスタム チャネル シンク ペアを構築することによって、常にエンドポイントを認証し、通信ストリームを暗号化してください。 |
注 : |
---|
このサンプルでは、ハードコーディングされたパスワードを使用しています。パスワードをハードコーディングするのは、どのような場合でも得策ではありません。ここでは、例を示すことだけを目的としてハードコーディングしています。パスワードをユーザーから取得するには、ユーザーに入力を求めるのが最も良い方法です。 |
このサンプルをコンパイルして実行するには
コマンド プロンプトで次のコマンドを入力します。
vbc /t:library /r:System.Runtime.Remoting.dll /out:PropSink.dll ChannelSinkPropertySetterProvider.vb vbc /t:library /r:System.Runtime.Remoting.dll serviceclass.vb vbc /r:System.Runtime.Remoting.dll /r:ServiceClass.dll /r:PropsSink.dll client.vb vbc /r:System.Runtime.Remoting.dll /r:ServiceClass.dll server.vb
csc /t:library /r:System.Runtime.Remoting.dll /out:PropSink.dll ChannelSinkPropertySetterProvider.cs csc /t:library /r:System.Runtime.Remoting.dll serviceclass.cs csc /r:System.Runtime.Remoting.dll /r:ServiceClass.dll /r:PropSink.dll client.cs csc /r:System.Runtime.Remoting.dll /r:ServiceClass.dll server.cs
同じディレクトリをポイントする 2 つのコマンド プロンプトを開きます。一方のコマンド プロンプトで「server」と入力します。これが実行されたら、もう一方のコマンド プロンプトで「client」と入力します。
このチャネル シンク プロバイダーは、プロバイダーのアクティビティのコンソール ダンプをクライアント コンソールに表示するかどうかを示す writeToConsole 属性をサポートしています。この例では、この属性は true に設定されています。
このアプリケーションは、1 台のコンピューター上で、またはネットワーク経由で実行されます。このアプリケーションをネットワーク経由で実行するには、クライアント構成の "localhost" をリモート コンピューターの名前に置き換えます。
ChannelSinkPropertySetterProvider
Imports System
Imports System.Collections
Imports System.IO
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
'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
Implements IClientChannelSinkProvider
Private _next As IClientChannelSinkProvider = Nothing
Private _channelSinkProperties As IDictionary = Nothing
Private _providerData As ICollection = Nothing
' Set 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
'Constructor with properties. If writeToConsole attribute is "true",
'this constructor will dump all custom configuration properties set
'in the configuration file.
Public Sub New(ByVal properties As IDictionary, ByVal providerData As ICollection)
_channelSinkProperties = properties
' Sets the private console dump property for this provider.
If (properties.Contains("writeToConsole")) Then
_consoleDump = Boolean.Parse(properties.Item("writeToConsole").ToString)
End If
_providerData = providerData
If (_consoleDump = True) 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
Dim child As Object
For Each child In sinkData.Children
Console.WriteLine("Child: " & child.GetType().Name)
Next
Next
Dim entry As DictionaryEntry
For Each entry In properties
Console.WriteLine("channel sink properties: " & entry.Key.ToString() & ", " & entry.Value.ToString())
Next
Console.WriteLine()
End If
End Sub
'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 System.Runtime.Remoting.Channels.IChannelSender, ByVal url As String, ByVal remoteChannelData As Object) As System.Runtime.Remoting.Channels.IClientChannelSink Implements System.Runtime.Remoting.Channels.IClientChannelSinkProvider.CreateSink
If (_consoleDump) Then
Console.WriteLine("CreateSink is called.")
Console.WriteLine("By " & channel.ChannelName)
End If
Dim nextSink As IClientChannelSink = Nothing
If (_next IsNot Nothing) Then
nextSink = _next.CreateSink(channel, url, remoteChannelData)
If (nextSink Is Nothing) Then
If (_consoleDump) Then
Console.WriteLine("Next sink is null!")
Return Nothing
End If
End If
WalkSinkChain(nextSink)
End If
Return nextSink
End Function
' 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("Walking the sink chain to find sink properties... ")
End If
While (thisSink IsNot Nothing)
If (_consoleDump) Then
Console.WriteLine(New String("_", 80))
Console.WriteLine("Next sink is : " & CType(thisSink, Object).GetType().Name)
DumpSinkProperties(thisSink)
End If
SetSinkProperties(thisSink)
thisSink = thisSink.NextChannelSink
End While
Return
End Sub
Private Sub DumpSinkProperties(ByVal sink As IClientChannelSink)
If (sink.Properties Is Nothing) Then
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
End Sub
' 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
Console.WriteLine("There are no properties available on the " & CType(sink.Properties, 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
Console.WriteLine("Setting sink property template on " & CType(sink, Object).GetType().Name & "." & entry.Key.ToString())
End If
sink.Properties.Item(entry.Key) = _channelSinkProperties.Item(entry.Key)
End If
Next
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
Console.WriteLine("Setting Instance override on " & CType(sink, Object).GetType().Name & "." & configEntry.Key)
End If
End If
sink.Properties.Item(configEntry.Key) = configEntry.Value
Next
Next
If (_consoleDump) Then
DumpSinkProperties(sink)
End If
End Sub
Public Property [Next]() As System.Runtime.Remoting.Channels.IClientChannelSinkProvider Implements System.Runtime.Remoting.Channels.IClientChannelSinkProvider.Next
Get
Return _next
End Get
Set(ByVal value As System.Runtime.Remoting.Channels.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 As IClientChannelSinkProvider = New SoapClientFormatterSinkProvider()
Dim sink As IClientChannelSinkProvider = chain
sink.Next = New BinaryClientFormatterSinkProvider()
sink = sink.Next
Return chain
End Function
End Class
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 = true;
// Default constructor.
public ChannelSinkPropertySetterProvider()
{
Console.WriteLine("Default constructor called.");
}
// 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();
}
}
// 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;
}
}
Client
Imports System
Imports System.Runtime.Remoting
Imports [Shared]
Public Class Client
Shared Sub Main()
RemotingConfiguration.Configure("Client.exe.config", False)
Dim service As ServiceClass = New ServiceClass()
Console.WriteLine(service.GetServerTime())
End Sub
End Class
using System;
using System.Runtime.Remoting;
public class Client
{
public static void Main(string[] Args)
{
RemotingConfiguration.Configure("Client.exe.config", false);
ServiceClass service = new ServiceClass();
Console.WriteLine(service.GetServerTime());
}
}
Server
Imports System
Imports System.Runtime.Remoting
Class Server
Shared Sub Main()
RemotingConfiguration.Configure("server.exe.config", False)
Console.WriteLine("Press enter to stop this process.")
Console.ReadLine()
End Sub
End Class
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
Imports System
Imports System.Runtime.Remoting
Public Class ServiceClass
Inherits MarshalByRefObject
Private starttime As DateTime
Public Sub New()
Console.WriteLine("A ServiceClass has been created.")
starttime = DateTime.Now
End Sub
Protected Overrides Sub Finalize()
Console.WriteLine("This object is being collected after " & (New TimeSpan(DateTime.Now.Ticks - starttime.Ticks)).ToString() & " seconds.")
End Sub
Public Function GetServerTime() As DateTime
Console.WriteLine("Time requested by a client.")
Return DateTime.Now
End Function
End Class
using System;
using System.Runtime.Remoting;
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">
<myElement 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, PropSink"
/>
</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>