Esempio di .NET Remoting: provider di sink di canale
Nell'esempio di codice seguente viene generato un oggetto ChannelSinkPropertySetterProvider
, che e` un provider di sink di canale in grado di leggere i valori del file di configurazione dell'applicazione e di percorrere lo stack di sink alla ricerca di sink di canale che supportino le proprieta` trovate nel file di configurazione. Tale procedura e` utile per generare una catena di sink con il file di configurazione anziche´ crearla a livello di programmazione e passare oggetti IDictionary ai costruttori.
Questo provider non inserisce sink nella catena di sink di canale, ma percorre semplicemente la catena di sink alla ricerca di proprieta` compatibili e puo` essere personalizzato per accettare solo le proprieta` supportate da un sink specifico.
Questo provider di esempio inoltre e` scritto e compilato in Visual Basic, mentre il resto dell'infrastruttura e` scritto nel linguaggio di programmazione C#. E` inclusa inoltre una versione C# del provider, che pero` non viene compilata mediante le righe di comando fornite.
Avviso
.NET Remoting non consente di eseguire l'autenticazione ne´ la crittografia per impostazione predefinita. E` pertanto consigliabile eseguire tutte le azioni necessarie per verificare l'identita` dei client o dei server prima di interagire con essi in modalita` remota. Poiche´ l'esecuzione delle applicazioni .NET Remoting richiede autorizzazioni FullTrust, se a un client non autorizzato venisse concesso l'accesso al server, il client potrebbe eseguire codice come se fosse completamente attendibile. E` necessario autenticare sempre gli endpoint e crittografare i flussi di comunicazione mediante l'hosting dei tipi remoti in Internet Information Services (IIS) oppure generando a questo scopo una coppia di sink di canale personalizzata.
Per compilare ed eseguire l'esempio
Al prompt dei comandi digitare i comandi seguenti:
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
Aprire due prompt dei comandi che puntino alla stessa directory. In uno, digitare server Quando e` in esecuzione, digitare client all'altro prompt dei comandi.
Questo provider di sink di canale supporta l'attributo writeToConsole per indicare se si desidera o meno un dump di console delle attivita` del provider nella console del client. In questo esempio, l'attributo e` impostato su true.
Questa applicazione viene eseguita in un singolo computer o in una rete. Per eseguire l'applicazione in una rete, nella configurazione del client sostituire localhost con il nome del computer remoto.
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
Questo file e` incluso solo a scopo informativo.
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>