FoxPress – Marzo 2002

 

.NET Remoting... y Fox

 

http://www.fpress.com/

 

Por Abel Sorzano                                               remoting.zip
 

En las aplicaciones monousuario que se ejecutan en una sola máquina los componentes en los que se basa esa aplicación residen en la propia máquina.  Pero en las aplicaciones distribuidas esto no sucede así pues los componentes necesitados por una aplicación cliente se pueden encontrar en otras máquinas.

 

La tecnología .NET y en concreto el .NET Framework facilita una infraestructura para desarrollar aplicaciones distribuidas basadas en componentes que se denomina .NET Remoting.

 

El .NET Remoting se aprovecha  de algunos servicios del .NET Framework que le permiten en primer lugar comunicarse mediante canales para enviar y recibir mensajes de aplicaciones remotas. Las aplicaciones pueden usar codificación binaria en aquellos lugares donde el rendimiento es importante o codificación XML donde lo importante es la interoperabilidad  con otros entornos remotos. Remoting se diseño teniendo en cuenta la seguridad, de esta forma puedes usar un número de ganchos para acceder a las llamadas de los mensajes y serializar las cadenas antes de que sean transportadas por el canal.

 

La infraestructura  del .NET Remoting es una aproximación abstracta al proceso de intercomunicación en el que los objetos se pueden pasar por valor o por referencia o copiados entre diferentes aplicaciones en entornos diferentes. Lo único que necesitas es marcar tu clase como serializable para poder realizar este trabajo.

 

La real fortaleza del Remoting, sin embargo, reside en la capacidad para permitir la comunicación entre objetos en diferentes aplicaciones o procesos usando diferentes protocolos de transporte, formatos de serialización, eschemas de durabilidad de los objetos, y modos de creación de los objetos. Además, si necesitas intervenir en cualquier punto del proceso de comunicación, por cualquier razón, el remoting lo hace posible.

 

Todo proceso de comunicación exige siempre que exista:

 

·   un servidor

·   un cliente

·   un canal de comunicaciones

 

Normalmente, todas las funciones que encapsulan los servidores funcionan bien en su propio proceso pero crean notables problemas a la hora de ser usados en otros procesos (cliente). Una forma de solucionar esto es copiar el objeto servidor enteramente al proceso del cliente y así sus métodos se invocan directamente. Sin embargo, muchos objetos no pueden o no deberían ser movidos a otro proceso para su ejecución. Por ejemplo, los objetos de gran tamaño pueden dar un rendimiento muy pobre cuando se copian al cliente o se pasan por valor  a otro proceso. Normalmente, un cliente necesita sólo la información devuelta por uno o unos pocos métodos del objeto servidor. Copiar todo el objeto servidor al cliente incluyendo información interna del objeto o estructuras de ejecución que no interesan para nada al cliente, ocuparía un gran ancho de banda junto con recursos de memoria y de procesador en el lado del cliente. Además muchos objetos exponen públicamente sus funcionalidades pero requieren datos privados para su ejecución interna. El hecho de que se copien esos objetos permitiría a los clientes maliciosos examinar los datos internos, creando notables problemas de seguridad. Finalmente, muchos objetos usan datos que simplemente no se pueden copiar de una forma inteligible. Un objeto FileInfo por ejemplo, contiene una referencia a un sistema operativo, que tiene una única dirección en el proceso de memoria del servidor. Puedes copiar esta dirección pero nunca tendrá sentido en otro proceso.

 

En estas situaciones el proceso servidor debería pasar al proceso del cliente una referencia al objeto servidor  no una copia del objeto. Los Clientes pueden usar esta referencia para llamar al objeto servidor. Estas llamadas no se ejecutarán en el proceso del cliente. En su lugar, el sistema remoto colecciona toda la información acerca del que llama y lo envía al proceso del servidor, donde es interpretado, el correcto objeto servidor es encontrado, ejecutado. El resultado de la llamada es enviada al cliente.  El ancho de banda se usa únicamente para la información crítica, la llamada, los necesarios argumentos, el valor de retorno y las excepciones.

 

 En el .NET Remoting los componentes que residen en el Cliente y los componentes del Servidor se comunican mediante canales.

 

 

Canales

 

Para que un Cliente se pueda comunicar con un objeto remoto necesitamos un camino a través del que se pueda realizar la transferencia de datos. Este camino se llama canal (Channel). Un canal toma una cadena de datos, crea un paquete  según las especificaciones de un protocolo y envía el paquete a otro ordenador. Algunos canales sólo reciben datos, otros sólo lo envían y finalmente otros pueden enviar y recibir como sucede con las clases TcpChannel  y HttpChannel que son bidireccionales.

 

Por tanto, tenemos dos tipos principales de canales:

 

1.- Canales TCP: : Como su nombre indica estos canales usan TCP para comunicarse. Los canales TCP transmiten datos en formato binario. Son ideales cuando el rendimiento es lo importante.

 

2.- Canales http: Estos canales usan HTTP para comunicarse. Lo más normal es que transporten mensajes de tipo SOAP. Son ideales cuando lo que prima es la interoperabilidad.

 

Estos canales están disponibles en los siguientes namespaces:

System.Runtime.Remoting.Channels.TCP
System.Runtime.Remoting.Channels.HTTP


A parte de estos canales se pueden crear todos los que se quiera para conseguir un mayor control en los procesos de transmisión.

 

Los Proxys

 

El Cliente por si mismo es incapaz de localizar al servidor y usa un objeto intermedio localizado en la máquina cliente que envía sus peticiones hacia el servidor remoto.

 

El uso de referencias a objetos para comunicar los objetos del servidor y los clientes es el corazón del remoting. Si configuras el cliente de forma adecuada, sólo necesitarás crear una nueva instancia del objeto remoto usando new. El cliente recibe una referencia al objeto del servidor, y  puedes a continuación llamar a sus métodos como si el objeto estuviera en tu propio proceso más que en otro PC distinto. Remoting usa objetos proxies para dar la impresión de que el objeto servidor se encuentra en el propio proceso del cliente. Cuando tu cliente crea una instancia del objeto remoto, la infraestructura remota crea un objeto proxy que se presenta ante el cliente como si fuera un objeto remoto. El cliente llama a un método de ese proxy, y el sistema remoto recibe la llamada y la ruta al proceso del servidor, invoca al objeto servidor, y devuelve el valor de retorno al cliente.

 


Como Funciona todo

Cuando la aplicación cliente se comunica con el objeto remoto se siguen los siguiente pasos:

 

En El Servidor:

 

1.- Se establece el Canal en un puerto específico que escucha todas las peticiones de los clientes.

 

2.- El objeto Remoto se autoregistra y declara su presencia en la red.

 

 

En el Cliente

 

1.- Se establece un canal de comunicaciones en un puerto específico.

 

2.- Se crea una instancia del objeto remoto mediante CREATEINstance o GetObject

 

3.- Se obtiene el Proxy para el objeto Remoto.

 

4.- El cliente llama al objeto remoto a través del Proxy.

 

 

Tipos de Objetos Remotos

 

Server-activated SingleCall Object:  para este tipo de objeto se creará una nueva instancia para cada método que sea invocado por el cliente.

 

Server-activated Singleton Object: para este tipo de objeto sólo existirá una instancia, independientemente de cuantos clientes estén dialogando con él.

 

Client-activated Object: para este tipo de objetos una nueva instancia se creará cuando el cliente realiza una llamada con new.

 

 

En nuestro ejemplo vamos a crear un objeto remoto que expone un método llamado HazPedido para insertar nuevos pedidos en pedidos.txt. Usa el sistema de activación en el servidor llamado SingleCall.:

 

En el servidor:

 

 

using System;

using System.IO;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

 

public class Pedido : MarshalByRefObject{

 

            public void HazPedido(string cli, int pro, int can){

                        DateTime dt = DateTime.Now;

                        string today = dt.ToString("dd-MMM-yyyy" );

                        string record = today+"|"+cli+"|"+pro+"|"+can;

                        StreamWriter sw = new StreamWriter("\\pedidos.txt",true);

                        sw.WriteLine(record);

                        sw.Close();

            }

}

 

public class PedidoServidor{

 

            public static void Start(){

                        ChannelServices.RegisterChannel(new TcpChannel(2255));

                        RemotingConfiguration.RegisterWellKnownServiceType(

                                                typeof(Pedido),

                                                "pedidos.rem",

                                                WellKnownObjectMode.SingleCall);

 

            }

            public static void Main(){

                        Start();

                        Console.WriteLine("pedido.rem Servicio Iniciado, pulse una tecla para terminar");

                        Console.ReadLine();

            }

}

 

 

Guarda este código como servidor.cs y compilalo como 

 

cs servidor.cs

 

Fíjate que la clase Pedido deriva del objeto System.MarshalByRef.  Todos los objetos remotos cuyas referencias se hacen disponibles a los clientes  (por medio de su proxy) deben derivar de esta clase. En el método main, creamos un canal TCP en el puerto  2255 y registramos el tipo Pedido. El nombre Pedidos.rem será usado por la URL del cliente para localizar este objeto.

 

 

Cliente:

 

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

 

public class OrderClient{

 

       public static void Main(){

 

              ChannelServices.RegisterChannel(new TcpChannel());

 

              Pedido remote = (Pedido) Activator.GetObject(

                                    typeof(Pedido), //Tipo de objeto remoto

                                    "tcp://localhost:2255/pedidos.rem"); //URL del objeto remoto

              Console.Write("Cliente: ");

              string cli = Console.ReadLine();

              Console.Write("Producto: ");

              int pro = Int32.Parse(Console.ReadLine());

              Console.Write("Cantidad: ");

              int can = Int32.Parse(Console.ReadLine());

              remote.HazPedido(cli,pro,can);

              Console.WriteLine("Pedido Realizado");

 

            }

 

}

 Guarda este código como Cliente.cs y compilalo con

csc cliente.cs

Para ejecutarlo, primero abre una ventana DOS y ejecuta el Servidor. En otra ventana DOS ejecuta el Cliente y vete siguiendo las instrucciones que pone. Al final te dejará en el raiz un fichero llamado Pedido.txt con el pedido que has realizado

 

También es posible instalar y ejecutar el Servidor como un Servicio de Windows 2000. Para hacer eso deberías crear dos clases como las que se ponen a continuación en Servicio2000.cs.

 

 

Creación de Servicio:

 

using System;

using System.ServiceProcess;

 

//Usado por Windows2000 service manager para run este service

public class OrderingWindowsService : ServiceBase{

            public OrderingWindowsService(){

                        ServiceName = "ServicioPedidos";

                        CanStop = true;

                        CanPauseAndContinue = true;

            }

             protected override void OnStart(string[] args){

                        PedidoServidor.Start();

                        EventLog.WriteEntry("ServicioPedidos started");

 

            }

            public static void Main(){

                        ServiceBase.Run(new OrderingWindowsService());

 

            }

}

 

 

//Usado por InstallUtil (una parte de .NET SDK) para configurar este servicio

[System.ComponentModel.RunInstaller(true)]

public class OrderingWindowsServiceInstaller : System.Configuration.Install.Installer{

 

             public OrderingWindowsServiceInstaller(){

 

                        ServiceProcessInstaller pi = new ServiceProcessInstaller();

                        pi.Account = ServiceAccount.LocalSystem; //Run under system account

                        ServiceInstaller  si = new ServiceInstaller();

                        //si.StartType = ServiceStartMode.Manual; //Requiere manual startup

                        si.StartType = ServiceStartMode.Automatic; //Se incia Automaticamente

                        si.ServiceName = "ServicioPedidos";

                        Installers.Add(si);

                        Installers.Add(pi);

 

            }

}

 

 

Compila: csc /r:servidor.exe servicio2000.cs

Instala: installutil servicio2000.exe

 

Para iniciar el servicio (el código anterior lo instala pero no lo activa) abre Herramientas Administratvas (disponible en Inicio/Programas/Herramientas Administrativas). Selecciona Servicios. Bajo Servicios y Aplicaciones, selecciona Servicio en el panel de la derecha y haz click en el botón de inicio de la barra de herramientas.

 

En todas estas pruebas... puede que te resulte un poco pesado tanta compilación. Para eso es mejor crearse un ficherito bat en el que compilas e instalas todo. Aquí lo tienes:

 

 

Compila.bat

 

installutil /u Servicio2000.exe

 

csc Servidor.cs

csc /r:Servidor.exe Servicio2000.cs

csc /r:Servidor.exe Cliente.cs

 

installutil Servicio2000.exe

 

 

Guardando los datos en tablas Fox

 

Pero demos un paso más:

 

Hasta ahora el Fox ha brillado por su ausencia... pero no podía tardar en aparecer. Vamos a hacer un pequeño cambio en el fichero servidor.cs para que nos inserte los datos en tablas Fox usando el OLEDB. (esto sólo funcionará con VFP 7. Si quieres usar Fox 6 deberías utilizar ODBC, para eso mírate el código que se puso en el número de Enero de 2002 de esta revista.

 

Podemos hacer el siguiente servidor en un fichero llamado ServidorFox.cs:

 

 

using System;

using System.IO;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

using System.Data;

using System.Data.OleDb;

 

public class Pedido : MarshalByRefObject{

 

            public void HazPedido(string cli, int pro, int can){

                        DateTime dt = DateTime.Now;

                        string today = dt.ToString("dd-MMM-yyyy" );

                       

                        OleDbConnection con=new OleDbConnection("Provider=VFPOLEDB.1;Data

                                  Source=C:\\revista\\Prueba4\\Bdatos.dbc");

                                          

                        string sql = "INSERT INTO TABLA1(cFecha, ccliente, nPro, nCan)

                            values('"+today+"','"+cli+"',"+pro+","+can+")";

                

                        OleDbCommand cmd = new OleDbCommand(sql,con);

                        cmd.Connection.Open();

                        int numRowsAffected = cmd.ExecuteNonQuery(); //

                                     devuelve 0 si ninguno es afectado

                         {

                             Console.WriteLine(numRowsAffected);

                      }

                

                      con.Close();

                      con.Dispose();

                      cmd.Connection.Close();

                      Console.ReadLine();

                 }

}

 

public class PedidoServidor{

 

            public static void Start(){

                        ChannelServices.RegisterChannel(new TcpChannel(2255));

                        RemotingConfiguration.RegisterWellKnownServiceType(

                                                typeof(Pedido),

                                                "pedidos.rem",

                                                WellKnownObjectMode.SingleCall);

 

            }

            public static void Main(){

                        Start();

                        Console.WriteLine("pedido.rem Servicio Iniciado, pulse una tecla para terminar");

                        Console.ReadLine();

            }

}

 

 

 

Para compilar esto tenemos que modificar nuestro ficherito bat a lo siguiente:

 

installutil /u Servicio2000.exe

 

csc ServidorFox.cs

csc /r:ServidorFox.exe Servicio2000.cs

csc /r:ServidorFox.exe Cliente.cs

 

installutil Servicio2000.exe

 

Además tenemos previamente que habernos creado una Base de Datos llamada Bdatos.dbc con una tabla Tabla1 y los campos (cFecha C(10), ccliente C(10), nPro N(5), nCan N(5)). El OLE DB no necesita configuración, excepto el path que tendrás que ajustarlo al de tu máquina. Fíjate que el arbol de directorios en ese path se separa con dos rallas.

 

 

 

 

FoxPress – Marzo de 2002

© 2002 FoxPress. All rights reserved