October 2nd, 2007Programación con C# y .NET Remoting/Mono Remoting [7/7]
Indice general de los manuales
- Introducción, instalación en Ubuntu y creación de una calculadora.
- Modos de compartición de objetos. Creando nuestra primera calculadora distribuida.
- Canales: TCP y HTTP. Configuración en archivo XML.
- Uso de interfaces para separar el código entre cliente y servidor. « [Leyendo]
Introducción
El objetivo en esta sección será separar el código del servidor y el del cliente. Durante estas prácticas hemos estado usando la biblioteca Calculo.dll tanto en el cliente como en el servidor, pero sería interesante que el cliente no tuviera la implementación de ésta. Nos pueden surgir varias ideas de por qué separar el código:
- Aunque el cliente tuviera el código de la biblioteca, en realidad está usando la clase remota que le proporciona el servidor, y no la suya.
- Puede que no queramos enseñar la implementación de la biblioteca, solo proporcionar el servicio a un cliente.
- Al no tener la implementación de la biblioteca, el cliente ocupa menos.
Así pues, aprenderemos tres mecanismos para separar el código:
Y también aprenderemos, ligeramente, el por qué de todo esto y que hace internamente .NET/Mono Remoting.
El código fuente de este tutorial te lo puedes bajar desde aquí.
1. Utilizando una interfaz
Un mecanismo muy utilizado en la POO es el uso de interfaces. Éstas nos sirven para obtener la parte pública de una clase sin necesidad de su implementación y después adjudicarle una clase que herede o extienda de esta interfaz.
Conociendo nuestra calculadora, podríamos definir la siguiente interfaz:
- // Interfaz de una calculadora
- public interface ICalculadora
- {
- // Properties de “laBase”
- double LaBase
- {
- get;
- set;
- }
- // POST: Suma dos números
- double Suma(double izq, double dcha);
- // POST: Divide dos números
- // EXCEPTION: si el denominador es 0
- double Divide(double numerador, double denominador);
- // POST: Eleva nuestra base al número que le pasamos en
- // el argumento
- double Potencia(double valor);
- }
Y ahora implementaremos nuestra clase calculadora para que extienda de esta interfaz, la cual tiene prácticamente el mismo código de la que hemos estado usando en las anteriores secciones:
- // Clase Calculadora que puede ser compartida de forma remota
- public class Calculadora : MarshalByRefObject, ICalculo.ICalculadora
- {
- // Atributo para usarlo en el metodo Potencia
- private double laBase;
- // Properties de “laBase”
- public double LaBase
- {
- get { return this.laBase; }
- set { this.laBase = value; }
- }
- /* bueno, y el resto del código que hemos usado anteriormente */
- }
Debemos prestar atención a que también herede de MarshalByRefObject para que el servidor la pueda compartir remotamente.
El código del servidor será igual que en las demás prácticas, prestando atención a que nuestra calculadora implementada que comparte el servidor tiene el espacio de nombres cambiado a CalculoConImplementacion y deberemos cambiarlo tanto si la compartimos usando código como con el archivo en XML (la manera que hayas elegido).
El código del cliente cambiará ligeramente y aprenderemos una nueva función llamada Activator.GetObject ya que no podemos crear un objeto con una interfaz.
- #region Bibliotecas
- // Biblioteca estandar
- using System;
- // Bibliotecas remoting
- using System.Runtime.Remoting;
- // Calculadora
- using Icalculo;
- #endregion
- public class Cliente
- {
- public static void Main (string [] args)
- {
- // Recogiendo el archivo de configuración del cliente.
- RemotingConfiguration.Configure(“Cliente.exe.config”, false);
- // Recogemos del archivo de configuración en XML, el
- // campo “RemotingUrl” que contiene la dirección del servidor
- System.Configuration.AppSettingsReader configurationAppSettings =
- new System.Configuration.AppSettingsReader();
- String url = ((string)(configurationAppSettings.GetValue(
- “RemotingUrl”,
- typeof(string))));
- // Creando el objeto
- Console.WriteLine(“Creando la calculadora con el codigo de la interfaz.”);
- ICalculadora calc = (ICalculadora)Activator.GetObject(
- typeof(ICalculo.ICalculadora),
- url);
- // Usamos sus metodos
- Console.WriteLine(“Suma 2+2 = “ + calc.Suma(2, 2));
- // Paramos
- Console.ReadLine();
- }
- }
2. Utilizando una clase abstracta
En realidad, una interfaz es muy parecida a una clase abstracta, solo se diferencian en: Una interfaz solo puede declarar constantes y métodos públicos. La clase abstracta puede implementar constantes y atributos públicos y métodos implementados o no. Una clase, en C#, solo puede derivar de otra, pero puede extender de varias interfaces.
Centrándonos en la segunda característica, nos daremos cuenta que anteriormente la clase implementada derivaba de MarshalByRefObject y extendía de ICalculadora, pero ahora nuestra calculadora implementada tendrá que derivar de MarshalByRefObject y de una clase abstracta (que llamaremos AbstractCalculadora). Al no poder derivar de más de una clase, haremos que nuestra clase abstracta derive de MarshalByRefObject y la implementada de la clase abstracta AbstractCalculadora.
La clase abstracta quedará de la siguiente forma:
- // Clase abstracta para una calculadora
- public abstract class AbstractCalculadora : MarshalByRefObject
- {
- // Properties de “laBase”
- public abstract double LaBase
- {
- get;
- set;
- }
- // POST: Suma dos numeros
- public abstract double Suma(double izq, double dcha);
- // POST: Divide dos numeros
- // EXCEPTION: si el denominador es 0
- public abstract double Divide(double numerador, double denominador);
- // POST: Eleva nuestra base al numero que le pasamos en
- // el argumento
- public abstract double Potencia(double valor);
- }
Y la clase implementada quedará así:
- public class Calculadora : AbstractCalculo.AbstractCalculadora
- {
- // Atributo para usarlo en el metodo Potencia
- private double laBase;
- // Properties de “laBase”
- public override double LaBase
- {
- get { return this.laBase; }
- set { this.laBase = value; }
- }
- /* … bueno, y el resto del código que hemos usado anteriormente … */
- }
Tanto el cliente como el servidor quedarán de la misma forma que en la sección anterior (utilizando una interfaz), ya que, como tampoco podemos crear un objeto de una clase abstracta, deberemos llamar a la función Activator.GetObject como hicimos anteriormente.
3. Utilizando una clase “vacía”
Este método será muy parecido a usar una clase abstracta, la única diferencia será en dejar los métodos no implementados. C# nos ofrece una excepción, para añadirla a métodos que no queremos implementar por el momento, llamada NotImplementExcepcion (que currado el nombre). Y que utilizaremos en nuestra clase vacía de la siguiente forma:
- // Clase vacia para una calculadora
- public class CalculadoraVacia : MarshalByRefObject
- {
- // Properties de “laBase”
- public virtual double LaBase
- {
- get { throw new NotImplementedException(); }
- set { throw new NotImplementedException(); }
- }
- // POST: Suma dos numeros
- public virtual double Suma(double izq, double dcha)
- {
- throw new NotImplementedException();
- }
- // POST: Divide dos numeros
- // EXCEPTION: si el denominador es 0
- public virtual double Divide(double numerador, double denominador)
- {
- throw new NotImplementedException();
- }
- // POST: Eleva nuestra base al numero que le pasamos en
- // el argumento
- public virtual double Potencia(double valor)
- {
- throw new NotImplementedException();
- }
- }
Como podemos observar, tenemos el mismo problema de herencia múltiple del caso de la clase abstracta, así que esta clase derivará de MarshalByRefObject y la siguiente derivará de CalculadoraVacia:
- public class Calculadora : CalculoVacio.CalculadoraVacia
- {
- // Atributo para usarlo en el metodo Potencia
- private double laBase;
- // Properties de “laBase”
- public override double LaBase
- {
- get { return this.laBase; }
- set { this.laBase = value; }
- }
- /* … bueno, y el resto del código que hemos usado anteriormente … */
- }
Ya tenemos nuestra clase para el cliente y para el servidor, pero, a diferencia de los otros mecanismos, en este sí que podemos instanciar un objeto de la clase vacía en el cliente (aunque no sirva de mucho, ya que usará la clase remota):
- public class Cliente
- {
- public static void Main (string [] args)
- {
- // Recogiendo el archivo de configuración del cliente.
- RemotingConfiguration.Configure(“Cliente.exe.config”, false);
- // Creando el objeto
- Console.WriteLine(“Creando la calculadora con el codigo de una clase abstracta.”);
- CalculadoraVacia calc = new CalculadoraVacia();
- // Usamos sus metodos
- Console.WriteLine(“Suma 2+2 = “ + calc.Suma(2, 2));
- // Paramos
- Console.ReadLine();
- }
- }
4. ¿Qué se hace internamente?
4.1. Clientes que se comunican con objetos remotos vía proxies.
Para no perdernos mientras leemos este apartado, tendremos el siguiente dibujo que nos ayudará en la comprensión:

Como hemos dicho varias veces durante esta sección y las anteriores, el cliente llama remotamente al objeto remoto. En realidad lo que hace el cliente es tener un representante (proxy) del objeto, conteniendo los métodos y atributos públicos que puede utilizar. Esta representación es el llamado TransparentProxy. Cuando, desde el cliente, se llama a un método del objeto, el TransparentProxy convierte la llamada en un objeto-mensaje que la describe, utilizando la clase Imessage, y la envía al llamado RealProxy. El RealProxy se encarga de reenviar el mensaje remotamente y así, el objeto remoto, utilizará esta información para ejecutar la acción.
Los objetos TransparentProxy y RealProxy se crean al instanciar un objeto que hace referencia a un objeto remoto y, tanto los mensajes como el RealProxy, se pueden ampliar y personalizar.
Esto ha sido un súper resumen sobre este funcionamiento, ideal para tener una visión general. Pero podemos consultar información más ampliada en la página del MSDN en la que explican esto con más detalle: http://msdn.microsoft.com/library/spa/default.asp?url=/library/SPA/dntaloc/html/hawkremoting.asp
4.2. Objetos sensibles al contexto
La siguiente pregunta a hacerse es sobre el operador new: ¿por qué a veces crea clases locales y otras remotas? En C# no es posible redefinir el operador new, pero la aplicación si puede obtener la información de como usar este operador dependiendo de la configuración que le proporcionemos. Esto es lo que diferencia a los objetos habituales (llamados ágiles) de los objetos sensibles al contexto (ContextBound). Los objetos sensibles al contexto son capaces de, utilizando clases intermedias, captar las llamadas que se producen a este objeto. Cuando decimos que un objeto es MarshalByRefObject le estamos diciendo que es un objeto sensible al contexto y tanto por el lado del cliente, como del servidor se crearán clases intermedias que administrarán la clase remota según en el ámbito en el que se encuentren:
- En el cliente: se crean los proxies y con estos se crean los mensajes, etc.
- En el servidor: se captan los mensajes y se llama adecuadamente al objeto compartido, etc.
Indice general de los manuales
- Introducción, instalación en Ubuntu y creación de una calculadora.
- Modos de compartición de objetos. Creando nuestra primera calculadora distribuida.
- Canales: TCP y HTTP. Configuración en archivo XML.
- Uso de interfaces para separar el código entre cliente y servidor. « [Leyendo]

October 19th, 2007 at 10:17 pm
Hola ERiDeM… he leido todo el manual de remoting mono y esta bien detallado y directo… derrepente no tan profundizado pero concizo (francamente no se… porque soy un novato en esto)… me preguntaba si sabias como ejecutar un programa que no sea programado en mono desde una aplicacion normal hecha de mono?… me dejo entender? … es decir que desde una aplicacion c# ejecute por ejemplo xmms o mplayer o algo parecido
October 21st, 2007 at 6:48 pm
Hola ferson,
Para ejecutar un programa externo desde un programa en C# tienes que usar la clase “Process”. Tienes que asegurarte que tienes Mono 2.0, que lo puedes instalar desde cualquier versión Debian (como Ubuntu) de la siguiente forma:
sudo aptitude install mono-gmcsY ahora el código de un programa de ejemplo:
// Programa a ejecutar
programa.StartInfo.FileName = "xmms";
// Argumentos que le pasamos al programa
programa.StartInfo.Arguments = "cancion.mp3"
// Lanzar el programa
programa.Start();
}
Espero que te haya servidor de ayuda. Te dejo dos links interesantes sobre esta clase:
Saludos!
October 27th, 2007 at 12:04 am
hola nuevamente…. la info que has posteado me ha sido util para meterme en lo que es procesos en c# …. gracias y sigue adelante con lo que haces… que nos viene bien para los novatos curiosos en sistemas distribuidos en .net (dentro de los cuales me incluyo).
Si no seria mucho abusar de tus conocimientos … puedes darme aunque sea un codigo de como crear un canal con mas seguridad que los que tiene System.Runtime.Remoting.Channels? … porque hasta donde yo se estos no aportan la seguridad necesaria y se que tengo que crear uno nuevo derivados de IChannelsSend, IChannelReceved y de IChannel creo … y gracias nuevamente
October 27th, 2007 at 5:47 pm
Hola ferson,
Pues ahora mismo no tengo ningun canal creado por mí, ya que, al igual que tu, yo soy uno de los curiosos de .NET Remoting y voy aprendiendo cosas día a día.
Lo que sí sé, es que los canales Tcp y Icp (este no le hemos visto en mi web) proporcionan encriptación en el envío de los mensajes si le habilitas la opción “IsSecure” a “true” en TcpChannel (o IcpChannel). Y también puedes crear una clase derivada de la interfaz “IAuthorizeRemotingConnection” para administrar usuarios. Para usar un canal Http, tienes que usar ISS para administrar los usuarios.
Siento mucho no haberte podido ayudar (de momento :P). En cuanto tenga tiempo, escribiré los manuales de la creación de canales personalizados y podremos debatir como hacer uno seguro.
Saludos.
PD. Si encuentras tu respuesta, te agradecería que me la pudieras contar. Thanks!
November 1st, 2007 at 10:05 pm
hola que tal.. despues de mucho tiempo vuelvo a eta web.
Ummm…sobre la personalizacion de un canal cliente-servidor, q creo que es vital para que exista seguridad en un sis distrib, todavia no lo he intentado, estoy leyendo informacion sobre eso mismo pero lo malo es que estan en .net de guindus, mas especifico sobre visual basic… y me cuesta adaptarlo porque no se mucho sobre visualbasic. De todas maneras pa los que dominan visual basic aqui coloco el link de un usernet para que ayuden no especificamente a mi sino al conocimiento intelectual de aquellos que se encuentran en la sombra aun (mejor dicho novatos … jejeje ta q me salio linda) …
http://bcngeeks.com/blogs/juan_cuello/archive/2007/07/27/autenticaci-243-n-personalizada-en-canal-encriptado-de-net-remoting-5-7.aspx
November 2nd, 2007 at 7:22 pm
Hola ferson,
Gracias por el link! La verdad es que no tengo nada de tiempo para el blog, ya que estoy haciendo un master y tengo un montón de prácticas por semana. Siempre es bien recibido que aportes/ayudes en el blog!
Espero poder continuarlo cuanto antes y aprender de todas las dudas que surgen.
Saludos!
November 3rd, 2007 at 6:07 pm
Hola, eridem quiero felicitarte por estos excelentes manuales estan muy bien expplicados y nos sirven de ayuda para muchos que no conozemos mucho de esto, bueno ojala y sigas publicando mas y felicidades.
November 4th, 2007 at 7:07 pm
Hola Jacob,
Me alegro de que te sean útiles y que te gusten mis explicaciones! Yo seguiré poco a poco haciéndolos, mientras tenga tiempo. Qué todavía tenemos muchas cosas pendientes!
Saludos!
November 7th, 2007 at 8:12 pm
Hola ERiDeM…excelentes articulos sobre remoting…solo una pregunta, sabes si es posible (como con dbus) gatillar un evento en el lado servidor y que se ejecute un manejador de eventos asociado al lado del cliente?
de antemano gracias…
Mauricio Henriquez
November 7th, 2007 at 8:21 pm
Creo que te refieres a una pregunta que se hizo anteriormente en http://eridem.net/programacion-con-c-y-net-remoting-mono-remoting-v/#comment-34.
Como no lo había explicado, cree un programa muy comentado que te puedes descargar aquí y que cada cliente manda su manejador al objeto del servidor para que este lo ejecute si lo cree necesario.
Saludos.
December 4th, 2007 at 9:08 pm
Hola,soy gerson y quisiera que me ayudaran a implementar una conexion de una base de datos Access a un programa
hecho en CSharp.No estoy dando el programa porque es un poco extenso, ademas, para hacer la conexion a una base de
datos primero he hecho un pequeño demo donde se demuestra la conexion. El codigo que se muestra abajo es muy comun…
cuando lo compilo…Excelente !!! (no me sale ningun error), pero cuando lo ejecuto me sale un error:
Unhandled Exception: System.DllNotFoundException: gda-2
at (wrapper managed-to-native) System.Data.OleDb.libgda:gda_init (string,string,int,string[])
at System.Data.OleDb.OleDbConnection..ctor () [0x00000]
at System.Data.OleDb.OleDbConnection..ctor (System.String connectionString)
at (wrapper remoting-invoke-with-check) System.Data.OleDb.OleDbConnection:.ctor(string)
at connect.Main () [0x00000]
Seria poco dejarles la informacion antes mencionada, por eso les dejo el codigo del demo. Si es que les sirve de informacion
adicional, la base de datos lo hice con Access 2003, ademas la conexion la hago con OleDb y utilizo la plataforma .NET libre
de Ximian (Mono) version 1.2.5.2
using System;
using System.Data;
using System.Data.OleDb;
public class Connect{
public static void Main(){
String connect = “Provider=Microsoft.JET.OLEDB.4.0;data source=.\\demo.mdb”;
OleDbConnection con = new OleDbConnection(connect);
con.Open();
Console.WriteLine(“Made the connection to the Sales database”);
con.Close();
}
}
===============
= Resultados =
===============
D:\mcs demo.cs -r:System.Data
December 4th, 2007 at 9:26 pm
Hola gerson,
Nunca he manejado bases de datos de Access con Mono, así que no te puedo dejar la solución. He estado buscando en la Internet (al igual que habrás hecho tú) y parece ser que no se sabe mucho de ese error.
Dentro de unos días tengo que implementar un programa que se va a conectar a una base de datos en MySQL, si te interesa pásate por el blog, pues haré un mini-tutorial de como hacerlo.
Saludos.
December 6th, 2007 at 12:30 am
Umm … bueno gracias de todas maneras. Sera motivo de investigar mas porque no pienso dejar eso en “queda alli”, en cuanto a lo que vas a hacer con mysql ya lo hice utilizando el conector quye da mysql … al menos que utilices otra manera de hacer la coneccion (si es asi pues estaria interesante).
Gracias nuevamente …
December 6th, 2007 at 9:40 am
Bueno, pues lo siento mucho :(
Yo te ayudaría a investigar, pero es que ahora tengo miles de trabajos pendientes y no tengo nada de tiempo. Cuando encuentres la solución nos lo cuentas :)
October 21st, 2009 at 11:59 pm
Sobre remoting :
Cuando haces la aplicacion cliente incluyes la
misma clase calculadora(implementado los mismos metodos ) que usas en el servidor.
esto siempre hay que hacerlo,de ser asi no le veo utilidad alguna..
October 22nd, 2009 at 12:08 pm
Lo
Si sigues el manual, te darás cuenta de que el cliente no implementa ningún método. Los métodos están implementados en la clase del servidor. Lo único que implementa el cliente son las cabeceras de los métodos (sin implementar). Esto debe ser así para que el cliente, al menos, sepa que posibles llamadas a métodos tiene la clase.
October 22nd, 2009 at 12:08 pm
Lo que dices no es cierto:
Si sigues el manual, te darás cuenta de que el cliente no implementa ningún método. Los métodos están implementados en la clase del servidor. Lo único que implementa el cliente son las cabeceras de los métodos (sin implementar por dentro). Esto debe ser así para que el cliente, al menos, sepa que posibles llamadas a métodos tiene la clase.
Saludos.