Receptores y cadenas de receptores
Los canales envían cada mensaje por una cadena de objetos receptores de canal antes de enviar o recibir un mensaje. Esta cadena de receptores contiene los receptores necesarios para la funcionalidad básica del canal, como receptores de formateador, de transporte o de creación de pilas, pero puede personalizar la cadena de receptores de canal para realizar tareas especiales con un mensaje o una secuencia.
Cada receptor de canal implementa IClientChannelSink o IServerChannelSink. El primer receptor de canal en el cliente también debe implementar IMessageSink. Normalmente, implementa IClientFormatterSink (que se hereda de IMessageSink, IChannelSinkBase e IClientChannelSink) y recibe el nombre de receptor de formateadores porque convierte el mensaje entrante en una secuencia (un objeto IMessage).
La cadena de receptores de canal procesa los mensajes enviados desde y hacia un dominio de aplicación. En este momento, sólo tiene un mensaje, pero podrá hacer lo que desee con él y el procesamiento posterior utilizará el mensaje que devuelva al sistema después del procesamiento. Éste constituye un lugar idóneo para implementar un servicio de registro, cualquier tipo de filtro o quizá cifrado o cualquier otro tipo de medidas de seguridad en el cliente o el servidor. En la ilustración siguiente se esquematiza la estructura de una cadena básica de receptores de canal.
Cadena básica de receptores de canal
Hay que tener en cuenta que cada receptor de canal procesa la secuencia y después la pasa al siguiente receptor de canal, lo que significa que los objetos anteriores o posteriores a su receptor deberían saber qué hacer con la secuencia que les ha pasado.
Nota
Los receptores de mensajes no deben iniciar excepciones. Una de las formas en las que un receptor de mensajes puede controlar eso es conteniendo el código de los métodos en bloques try-catch.
Los proveedores de receptores de canal (objetos que implementan la interfaz IClientChannelSinkProvider, IClientFormatterSinkProvider o IServerChannelSinkProvider) son responsables de crear los receptores de canal a través de los cuales pasan los mensajes de interacción remota. Cuando se activa un tipo remoto, el proveedor de receptores de canal se extrae del canal y se llama al método CreateSink en el proveedor de receptores para que extraiga el primer canal del receptor desde la cadena.
Los receptores de canal son los responsables de transportar los mensajes entre el cliente y el servidor. También están vinculados entre sí en una cadena. Cuando se llama al método CreateSink en un proveedor de receptores, dicho método debería hacer lo siguiente:
Crear su propio receptor de canal.
Llamar a CreateSink en el siguiente proveedor de receptores de la cadena.
Garantizar que el siguiente receptor y el actual están vinculados.
Devolver su receptor al llamador.
Los receptores de canal son los responsables de reenviar todas las llamadas que reciben al siguiente receptor de la cadena y deben proporcionar un mecanismo para almacenar una referencia al siguiente receptor.
Los receptores de canal tienen una gran flexibilidad con respecto a lo que envían a lo largo de la cadena de receptores. Por ejemplo, los receptores de seguridad que desean establecer negociaciones de autenticación antes de enviar el mensaje original serializado propiamente dicho pueden retener todo el mensaje de canal, reemplazar la secuencia por su propio contenido y enviar el mensaje a lo largo de la cadena de receptores y de ahí al dominio de aplicación remoto. En el trayecto de vuelta, el receptor de seguridad puede interceptar el mensaje de respuesta y establecer una conversación con los correspondientes receptores de seguridad en el dominio de aplicación remoto. Cuando se llega a un acuerdo, el receptor de seguridad de origen puede enviar la secuencia original al dominio de aplicación remoto.
Procesamiento de mensajes en la cadena de receptores de canal
Una vez que el sistema .NET Remoting encuentra un canal que puede procesar la implementación de IMethodCallMessage, dicho canal llama a IMessageSink.SyncProcessMessage (o a IMessageSink.AsyncProcessMessage) para pasar el mensaje al receptor de canal de formateador. El receptor de formateador crea la matriz de encabezados de transporte y llama a IClientChannelSink.GetRequestStream en el siguiente receptor. Esta llamada se reenvía a lo largo de la cadena de receptores y cualquier receptor puede crear una secuencia de solicitud que se pasará de vuelta al receptor de formateador. Si GetRequestStream devuelve una referencia nula (Nothing en Visual Basic), el receptor de formateador crea su propio receptor para la serialización. Cuando se devuelve esta llamada, se serializa el mensaje y se llama al método de procesamiento de mensajes apropiado en el primer receptor de canal de la cadena de receptores.
Los receptores no pueden escribir datos en la secuencia, pero pueden leerla o pasar una nueva cuando es necesario. Los receptores también pueden agregar encabezados a la matriz de encabezados (si antes no han llamado a GetRequestStream en el siguiente receptor) y agregarse ellos mismos a la pila de receptores antes de reenviar la llamada al siguiente receptor. Cuando la llamada llega al receptor de transporte al final de la cadena, éste envía los encabezados y el mensaje serializado a través del canal al servidor, donde se invierte todo el proceso. El receptor de transporte (en el servidor) recupera los encabezados y el mensaje serializado desde el servidor y los reenvía a lo largo de la cadena de receptores hasta llegar al receptor de formateador. El receptor de formateador deserializa el mensaje y lo reenvía al sistema de interacción remota, donde es convertido de nuevo en una llamada a un método y se invoca en el objeto de servidor.
Crear cadenas de receptores de canal
Para crear un nuevo receptor de canal, debe implementar y configurar el sistema de interacción remota de manera que reconozca una implementación IServerChannelSinkProvider o IClientChannelSinkProvider, que pueden crear sus implementaciones IClientChannelSink o IServerChannelSink personalizadas o recuperar el siguiente receptor de la cadena. Puede usar la clase abstracta BaseChannelSinkWithProperties como ayuda para implementar sus receptores de canal personalizados.
Crear un proveedor de receptores de canal
Las aplicaciones pueden proporcionar proveedores de receptores de canal en el servidor o en el cliente como parámetros en el momento de construir un canal. Los proveedores de receptores de canal se deben almacenar en una cadena y el usuario es responsable de encadenar todos los proveedores de receptores de canal antes de pasar el externo al constructor del canal. Para ello, el proveedor de receptores de canal implementa una propiedad Next. En el ejemplo de código siguiente se muestra cómo crear un proveedor de receptores de canal en el cliente. Encontrará un ejemplo completo en Ejemplo de interacción remota: Proveedor de receptores de canal.
private Function CreateDefaultClientProviderChain() As IClientChannelSinkProvider
Dim chain As New FirstClientFormatterSinkProvider
Dim sink As IClientChannelSinkProvider
sink = chain
sink.Next = New SecondClientFormatterSinkProvider
sink = sink.Next
return chain
End Function
private IClientChannelSinkProvider CreateDefaultClientProviderChain(){
IClientChannelSinkProvider chain = new FirstClientFormatterSinkProvider();
IClientChannelSinkProvider sink = chain;
sink.Next = new SecondClientFormatterSinkProvider();
sink = sink.Next;
return chain;
}
Nota
Cuando en un archivo de configuración se especifican varios proveedores de receptores de canal, el sistema de interacción remota los encadena en el mismo orden en el que se encuentran en el archivo de configuración. Los proveedores de receptores de canal se crean al crear el canal durante la llamada a RemotingConfiguration.Configure.
Receptores de formateadores
Los receptores de formateadores serializan el mensaje de canal en la secuencia de mensajes como un objeto que implementa IMessage. Algunas implementaciones de receptores de formateadores utilizan los tipos de formateador proporcionados por el sistema (BinaryFormatter y SoapFormatter). Otras implementaciones pueden utilizar sus propios medios para transformar el mensaje de canal en la secuencia.
La función del receptor de formateador es generar los encabezados necesarios y serializar el mensaje en la secuencia. Después del receptor de formateador, el mensaje se reenvía a todos los receptores de la cadena de receptores mediante llamadas a IMessageSink.ProcessMessage o Imessagesink.AsyncProcessMessage. En esta fase, el mensaje ya se ha serializado y se proporciona sólo como información.
Nota
Los receptores que necesitan crear o modificar el mensaje propiamente dicho deben situarse en la cadena de receptores antes del formateador. Esto se puede conseguir fácilmente si se implementa IClientFormatterSink, con lo que se hace creer al sistema que tiene una referencia al receptor de formateador. El verdadero receptor de formateador se puede situar en la cadena de receptores en una posición posterior.
En el trayecto de vuelta, el receptor de formateador transforma la secuencia de mensaje de nuevo en los elementos del mensaje de canal (mensaje devuelto). El primer receptor en el cliente debe implementar la interfaz IClientFormatterSink. Cuando CreateSink vuelve al canal, la referencia devuelta se convierte en un tipo IClientFormatterSink para que se pueda llamar al SyncProcessMessage de la interfaz IMessage. Si se produce un error en la conversión, el sistema inicia una excepción.
Receptores de canal personalizados
En el cliente, se insertan receptores de canal personalizados en la cadena de objetos entre el receptor de formateador y el último receptor de transporte. Insertar un receptor de canal personalizado en el canal de cliente o de servidor le permite procesar el IMessage en uno de estos dos puntos:
Durante el proceso en el que una llamada representada por un mensaje se convierte en una secuencia y se envía a través de la conexión.
Durante el proceso en el que una secuencia es extraída de la conexión y enviada al objeto StackBuilderSink (el último receptor de mensajes anterior al objeto remoto en el servidor) o al objeto proxy (en el cliente).
Los receptores personalizados pueden leer o escribir datos (dependiendo de si la llamada es saliente o entrante) en la secuencia y agregar información adicional a los encabezados cuando así se desee. En esta fase, el formateador ya ha serializado el mensaje y no se puede modificar. Cuando la llamada del mensaje es reenviada al receptor de transporte al final de la cadena, éste escribe los encabezados en la secuencia y la reenvía al receptor de transporte del servidor mediante el protocolo de transporte indicado por el canal.
Receptores de transporte
El receptor de transporte es el último receptor en la cadena del cliente y el primero en la cadena del servidor. Además de transportar mensajes serializados, el receptor de transporte también es responsable de enviar los encabezados al servidor y de recuperar los encabezados y la secuencia cuando el servidor devuelve la llamada. Estos receptores están integrados en el canal y no se pueden extender.
Reemplazar el formateador predeterminado
Debido a que el canal es un mecanismo de red abstracto, puede configurar el sistema .NET Remoting de manera que combine un canal implementado por el sistema con el formateador que usted elija. Para ello, puede usar el constructor de canales que emplea una implementación IDictionary de las propiedades de canal, un formateador en el servidor y un formateador en el cliente. También puede especificar el formateador en un archivo de configuración. En el ejemplo siguiente se indica al sistema de configuración de .NET Remoting que cree un HttpChannel pero utilice BinaryClientFormatterSink en el cliente.
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http">
<clientProviders>
<formatter ref="binary"/>
</clientProviders>
<channels>
</application>
</system.runtime.remoting>
</configuration>
En el código siguiente se hace lo mismo mediante programación, suponiendo un tipo de interfaz remota IService que implementa GetServerString y GetServerTime.
Imports System
Imports System.Collections
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
Public Class ClientProcess
<MTAThread()> _
Public Shared Sub Main()
' Note that any name/value pairs of configuration attributes can be
' placed in this dictionary (the configuration system calls this same
' constructor).
Dim properties As New Hashtable()
properties("name") = "HttpBinary"
ChannelServices.RegisterChannel(New HttpChannel(properties, New BinaryClientFormatterSinkProvider(), Nothing))
' The last parameter above (Nothing) is the server sink provider chain
' to obtain the default behavior (which includes SOAP and
' binary formatters on the server side).
Dim service As IService = CType(Activator.GetObject(GetType(IService), "http://computer:8080/SAService"), IService)
Console.WriteLine("Server string is: " + service.GetServerString())
Console.WriteLine("Server time is: " + service.GetServerTime())
End Sub
End Class
using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
public class ClientProcess{
public static void Main(string[] Args){
// Note that any name/value pairs of configuration attributes can be
// placed in this dictionary (the configuration system calls this
// same HttpChannel constructor).
IDictionary properties = new Hashtable();
properties["name"] = "HttpBinary";
// The last parameter below is the server sink provider chain
// to obtain the default behavior (which includes SOAP and binary
// formatters) on the server side.
ChannelServices.RegisterChannel(new HttpChannel(null, new BinaryClientFormatterSinkProvider(), null));
IService service = (IService)Activator.GetObject(typeof(IService),"http://computer:8080/SAService");
Console.WriteLine("Server string is: " + service.GetServerString());
Console.WriteLine("Server time is: " + service.GetServerTime());
}
}
Para obtener un ejemplo completo de esta combinación de canal y formateador alojada en Servicios de Internet Information Server (IIS), vea Ejemplo de interacción remota: Alojar en Servicios de Internet Information Server (IIS).
Para modificar este cliente de manera que use un objeto TcpChannel con el objeto SoapClientFormatterSink, sólo modificar los espacios de nombres y la llamada a RegisterChannel, tal y como se indica en el ejemplo de código siguiente.
ChannelServices.RegisterChannel(New TcpChannel(properties, New SoapClientFormatterSinkProvider(), Nothing))
ChannelServices.RegisterChannel(new TcpChannel(null, new SoapClientFormatterSinkProvider(), null));
Vea también
Conceptos
Alojar objetos remotos en Servicios de Internet Information Server (IIS)
Ejemplo de interacción remota: Alojar en Servicios de Internet Information Server (IIS)