Compartir a través de


Ejemplo de interacción remota: Proveedor de receptores de canal

En este ejemplo de código se crea un ChannelSinkPropertySetterProvider, que es un proveedor de receptores de canal que puede leer valores en el archivo de configuración de la aplicación y examinar la pila de receptores en busca de receptores de canal que admitan las propiedades que encontró en el archivo de configuración. Ésta puede ser una forma útil de usar el archivo de configuración para crear una cadena de receptores en vez de crearla mediante programación y pasar los objetos IDictionary a los constructores.

Este proveedor no inserta ningún receptor en la cadena de receptores de canal. Se limita a examinar la cadena de receptores en busca de propiedades compatibles y se puede personalizar fácilmente de manera que adopte sólo las propiedades admitidas por un determinado receptor.

Además, este proveedor de ejemplo está escrito y se compila en Visual Basic, mientras que el resto de la infraestructura está escrita en C#. También se incluye una versión C# del proveedor con fines informativos, pero las líneas de comandos dadas no la compilan.

PRECAUCIÓN   .NET remoting no autentica ni codifica de manera predeterminada. Por lo tanto, se recomienda que siga todos los procedimientos necesarios para asegurarse de la identidad de los clientes o servidores antes de interactuar con ellos de manera remota. Como las aplicaciones de .NET remoting requieren permisos FullTrust para ejecutarse, si a un cliente no autorizado se le ha concedido acceso en su servidor, el cliente podrá ejecutar código como si fuera de total confianza. Autentique siempre los extremos y codifique las secuencias de comunicación, ya sea alojando en Servicios de Internet Information Server (IIS) los tipos utilizados de forma remota o generando un par de receptores de canales personalizados para que se hagan cargo de este trabajo.

Para compilar y ejecutar este ejemplo

  1. Escriba los siguientes comandos en el símbolo del sistema:

    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. Abra dos símbolos del sistema que apunten al mismo directorio. En uno de ellos, escriba server. Una vez que se esté ejecutando, escriba client en el otro símbolo del sistema.

Este proveedor de receptores de canal admite el atributo writeToConsole para indicar si desea un volcado en la consola de las actividades del proveedor en la consola de cliente. Para este ejemplo, el atributo está establecido en true.

Esta aplicación se ejecuta en un único equipo o a través de una red. Si desea ejecutar esta aplicación a través de una red, debe reemplazar "localhost" en la configuración del cliente con el nombre del equipo remoto.

ChannelSinkPropertySetterProvider.vb

ChannelSinkPropertySetterProvider.cs

Este archivo se incluye sólo con fines informativos.

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>

Vea también

Ejemplos de interacción remota | Canales | Receptores y cadenas de receptores