.NET Remoting... y Fox
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.
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.
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
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