c# .net remoting

c# - En.NET remoting, ¿cuál es la diferencia entre RemotingConfiguration.RegisterWellKnownServiceType y RemotingServices.Marshal?



(3)

En .NET remoting, ¿cuál es la diferencia entre RemotingConfiguration.RegisterWellKnownServiceType y RemotingServices.Marshal?

Lo que quiero hacer es crear un objeto en un servicio de Windows, luego ponerlo como un objeto remoto y hacer que el servicio de Windows y el cliente actúen sobre el objeto remoto.

Pensé que el código a continuación lograría esto.

FooRemoting foo = new FooRemoting(); RemotingConfiguration.RegisterWellKnownServiceType(typeof(FooRemoting), serverName, WellKnownObjectMode.Singleton); RemotingServices.Marshal(foo);


Esto es lo que encontré.

RemotingConfiguration.RegisterWellKnownServiceType(typeof(FooRemoting), serverName, WellKnownObjectMode.Singleton);

RegisterWellKnownServiceType creará el objeto y lo convertirá en Singleton para cualquier cliente que lo consuma, pero no se creará una referencia por parte del servidor. El objeto no se crea hasta que un cliente lo solicite, y el mismo objeto se usa para otros clientes.

RemotingServices.Marshal(foo);

Marshal registrará un objeto que ha sido creado por el servidor, en este caso un servicio de Windows. Entonces el servidor tendrá referencia al objeto y los clientes consumirán el mismo objeto.

Mi problema era usar el Marshal para registrar el objeto remoto. Con el tiempo, el objeto remoto desaparecerá para que los clientes consuman, es decir, ya no esté en el objeto remoto. El servicio aún mantendría su referencia. Luego probé RegisterWellKnownServiceType y los clientes siguen obteniendo la referencia correcta, sin embargo, no pude conseguir que el servicio tuviera una referencia al mismo objeto.

La solución está anulando el objeto remoto en este caso FooRemoting. Si sobreescribo el InitializeLifetimeService y devuelvo el valor nulo, el cliente nunca perderá la conexión y el servicio mantendrá la conexión.

public override object InitializeLifetimeService() { //return base.InitializeLifetimeService(); return null; }

Para mantener el objeto creado por el servicio y hacer que el cliente use el mismo objeto, debe usar

RemotingServices.Marshal(foo);

y anula InitializeLifetimeService para devolver nulo.


Hice un experimento con RemotingServices.Marshal como este

Componente remotable alojado en un Exe de Windows. El código Exe es

Form1_Load(object sender, EventArgs e) { RemotingConfiguration.Configure("path of the config file"); RemoteClass obj = new RemoteClass(); obj.MyVal =100; RemotingServices.Marshal(obj); } public RemoteClass: MarshalByRefObj { static int Counter; public RemoteClass() { Counter++; } int _MyVal =0; public int MyVal { get { return _MyVal; } set { _MyVal = value; } } }

Ahora en el código del lado del cliente

button1_click() { RemoteClass obj = Activator.GetObject(typeof(RemoteClass), "object URI"); if(RemotingServices.IsTransparentProxy(obj)) { MessageBox.Show(obj.Myval.ToString()); } }

Aparecerá el mensaje como 0 no 100. Si coloca un punto de interrupción en el constructor de RemoteClass, verá que el constructor recibe una llamada 2 veces

  1. Cuando el objeto RemoteClass se crea en el Servicio mismo
  2. Cuando el cliente realiza una llamada a la propiedad MyVal.

Creo que RemotingServices.Marshal no tiene nada que ver con la única instancia. Incluso si usa solo RemotingConfiguration.Configure y reemplaza InitializeLifetimeService para que devuelva null, será suficiente para alojar un componente remotoable.


Es posible exponer MarshalByRefObjects que tienen constructores con parámetros a distancia, y es posible que los usuarios de la clase solo se ocupen de su interfaz.

He creado un pequeño proyecto de prueba de concepto. Tiene 3 proyectos: Servidor, Cliente y Core. El servidor y el cliente hacen referencia al núcleo pero no se referencian entre sí.

En núcleo, definimos una interfaz de servicio:

namespace Core { public interface ICountingService { int Increment(); } }

El servidor define la implementación concreta, que el cliente no tiene una referencia :

namespace Server { public class CountingService : MarshalByRefObject, ICountingService { private static int _value = 0; public CountingService(int startValue) { _value = startValue; } public int Increment() { // not threadsafe! _value++; return _value; } } }

Los aspectos importantes a tener en cuenta son que tiene un constructor con un parámetro, es un MarshalByRefObject e implementa la interfaz en el proyecto principal.

El proyecto de servidor es una aplicación de consola que configura un canal remoto (arbitrariamente sobre HTTP para este ejemplo), crea el servicio y lo registra con comunicación remota:

using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; namespace Server { class Program { static void Main(string[] args) { HttpServerChannel serverChannel = new HttpServerChannel(8234); ChannelServices.RegisterChannel(serverChannel, false); // Following line won''t work at runtime as there is no parameterless constructor //RemotingConfiguration.RegisterWellKnownServiceType(typeof(CountingService), // "CountingService.rem", WellKnownObjectMode.Singleton); CountingService countingService = new CountingService(5); RemotingServices.Marshal(countingService, "CountingService.rem"); Console.WriteLine("Press enter to exit."); Console.ReadLine(); } } }

El código anterior ha registrado la URL http: // localhost: 8234 / CountingService.rem que contiene el servicio instanciado, que comenzará a contar desde 5.

El cliente, también una aplicación de consola, puede obtener una referencia, utilizando la clase de interfaz:

using System; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using Core; namespace Client { class Program { static void Main(string[] args) { HttpClientChannel serverChannel = new HttpClientChannel(); ChannelServices.RegisterChannel(serverChannel, false); for (int i = 0; i < 5; i++) { ICountingService countingService = (ICountingService)Activator.GetObject(typeof(ICountingService), "http://localhost:8234/CountingService.rem"); int newValue = countingService.Increment(); Console.WriteLine("Value is " + newValue); } Console.WriteLine("Press enter to exit."); Console.ReadLine(); } } }

Cuando se ejecutan el servidor y el cliente, imprime valores del 6 al 10.

Resumen: el cliente solo conoce la interfaz; el constructor de implementación puede tener parámetros; la instanciación puede ser controlada por su propio código en lugar de .NET. Muy útil cuando se trata de inyección de dependencia basada en el constructor con objetos remotos.