Freigeben über


Remotingbeispiel: Channelempfängeranbieter

Mit dem folgenden Codebeispiel wird ChannelSinkPropertySetterProvider erstellt. Dies ist ein Channelempfängeranbieter, der Werte in der Anwendungskonfigurationsdatei lesen und den Empfängerstapel nach Channelempfängern durchsuchen kann, die die in dieser Datei enthaltenen Eigenschaften unterstützen. Dies ist nützlich, um eine Empfängerkette unter Verwendung der Konfigurationsdatei zu erstellen, anstatt die Kette programmgesteuert zu erstellen und IDictionary-Objekte an die Konstruktoren zu übergeben.

Dieser Anbieter fügt keinen Empfänger in die Channelempfängerkette ein. Vielmehr wird nur die Empfängerkette nach kompatiblen Eigenschaften durchsucht. Außerdem kann der Anbieter problemlos angepasst werden, sodass nur die von einem bestimmten Empfänger unterstützten Eigenschaften akzeptiert werden.

Des Weiteren ist dieser Beispielanbieter in Visual Basic geschrieben und kompiliert, während der Rest der Infrastruktur in der Programmiersprache C# geschrieben ist. Eine C#-Version des Anbieters ist zwar ebenfalls enthalten, wird aber durch die angegebenen Befehlszeilen nicht kompiliert.

Warnung

.NET Remoting führt standardmäßig keine Authentifizierung oder Verschlüsselung aus. Daher empfiehlt es sich, alle erforderlichen Schritte auszuführen, um die Identität von Clients und Servern vor der Remoteinteraktion eindeutig zu überprüfen. Da für die Ausführung von .NET Remoting-Anwendungen die Berechtigung FullTrust erforderlich ist, könnte ein nicht autorisierter Client, dem der Zugriff auf den Server gewährt wurde, Code so ausführen, als ob er vollständig vertrauenswürdig wäre. Authentifizieren Sie stets die Endpunkte, und verschlüsseln Sie die Kommunikationsstreams, entweder durch Hosten der Remotetypen in Internetinformationsdienste (Internet Information Services, IIS), oder indem Sie für diese Aufgabe ein benutzerdefiniertes Channelempfängerpaar erstellen.

So kompilieren Sie dieses Beispiel und führen es aus

  1. Geben Sie an der Eingabeaufforderung die folgenden Befehle ein:

    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. Öffnen Sie zwei Eingabeaufforderungen, die auf dasselbe Verzeichnis zeigen. Geben Sie an der einen Eingabeaufforderung server ein. Sobald dies ausgeführt wird, geben Sie an der anderen Eingabeaufforderung client ein.

Dieser Channelempfängeranbieter unterstützt das writeToConsole-Attribut, mit dem angegeben wird, ob die Aktivitäten des Anbieters in der Clientkonsole ausgegeben werden. In diesem Beispiel ist das Attribut auf true festgelegt.

Diese Anwendung wird auf einem einzelnen Computer oder über ein Netzwerk ausgeführt. Wenn Sie die Anwendung über ein Netzwerk ausführen möchten, müssen Sie "localhost" in der Clientkonfiguration durch den Namen des Remotecomputers ersetzen.

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

Diese Datei wird nur zur Information bereitgestellt.

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>

Siehe auch

Konzepte

Channel
Empfänger und Empfängerketten

Weitere Ressourcen

Remotingbeispiele