September 20th, 2007Programación con C# y .NET Remoting/Mono Remoting [6/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. « [Leyendo]
- Uso de interfaces para separar el código entre cliente y servidor.
Introducción
Ya que tenemos una buena base, e incluso ya podríamos hacer nuestros programillas, ahora estudiaremos algunas cosas que nos pueden ser interesantes y de gran ayuda. Aprenderemos dos cosas:
- Compartiremos los objetos mediante el canal TCP, en lugar de HTTP como hemos ido haciendo anteriormente.
- Guardaremos la configuración de como comparte el servidor los objetos en un archivo, ya que, de esta forma, no tendremos que compilar el servidor o el cliente) cada vez que decidamos si queremos un SingleCall, un Singleton o un CAO; y también la dirección, el puerto, etc.
1. Usando el canal TCP
Usar el canal TCP es tan fácil como usar HTTP. De hecho, si echamos un vistazo a códigos anteriores, podríamos intuir cómo usarlo.
Cogeremos el código del servidor de alguno de los ejemplos anteriores para ver el cambio:
- // Archivo: Servidor.cs
- // Descripcion: programa servidor que comparte
- // la clase Calculadora remotamente.
- // Biblioteca estandar y remoting
- using System;
- using System.Runtime.Remoting;
- // Clases de Remoting
- using System.Runtime.Remoting.Channels;
- using System.Runtime.Remoting.Channels.Http;
- namespace Servidor
- {
- public class Servidor
- {
- // arg[0] representa el puerto del servidor
- public static void Main(string[] args)
- {
- // Creamos un nuevo objeto para el canal HTTP
- HttpChannel chnl = new HttpChannel(int.Parse(args[0]));
- // Le decimos a la máquina que nos reserve ese canal
- ChannelServices.RegisterChannel(chnl);
- // Opciones para la comparticion de la clase
- RemotingConfiguration.RegisterWellKnownServiceType(
- typeof(Calculo.Calculadora),
- “Calculadora.remota”,
- WellKnownObjectMode.SingleCall);
- // Mensajes de espera en la pantalla
- Console.WriteLine(“Atendiendo las peticiones…”);
- Console.WriteLine(“Pulse Enter para salir…”);
- Console.ReadLine();
- }
- }
- }
Como vemos en el ejemplo, estamos usando siempre HTTP. El código para usar el canal TCP consistirá en cambiar las apariciones de “Http” por “Tcp”, así de simple. Podemos verlo en el siguiente ejemplo del código anterior modificado:
- // Archivo: Servidor.cs
- // Descripcion: programa servidor que comparte
- // la clase Calculadora remotamente.
- // Biblioteca estandar y remoting
- using System;
- using System.Runtime.Remoting;
- // Clases de Remoting
- using System.Runtime.Remoting.Channels;
- using System.Runtime.Remoting.Channels.Tcp;
- namespace Servidor
- {
- public class Servidor
- {
- // arg[0] representa el puerto del servidor
- public static void Main(string[] args)
- {
- // Creamos un nuevo objeto para el canal TCP (antes era HTTP)
- TcpChannel chnl = new TcpChannel(int.Parse(args[0]));
- // Le decimos a la máquina que nos reserve ese canal
- ChannelServices.RegisterChannel(chnl);
- // Opciones para la comparticion de la clase
- RemotingConfiguration.RegisterWellKnownServiceType(
- typeof(Calculo.Calculadora),
- “Calculadora.remota”,
- WellKnownObjectMode.SingleCall);
- // Mensajes de espera en la pantalla
- Console.WriteLine(“Atendiendo las peticiones…”);
- Console.WriteLine(“Pulse Enter para salir…”);
- Console.ReadLine();
- }
- }
- }
1.1. ¿En qué se diferencian los dos canales?
Canal HTTP: Los mensajes que recibimos y envía el objeto remoto usarán el protocolo SOAP, es decir, estos datos se convertirán en formato SOAP (Simple Object Access Protocol) para comunicarse entre sí. El protocolo SOAP es una representación de la comunicación entre dos objetos en XML. Así pues, cuando los programas se comunicaban entre sí, lo hacían enviándose mensajes en XML que podían interpretar.
Canal TCP: es más simple, los mensajes que se usan para la comunicación se envían en binario.
1.2. ¿Cuál usar?
Mirando esta tabla, podremos escoger nuestro canal según nuestra necesidad:
| Causa | HTTP | TCP |
| Rapidez | Es más lento, ya que tiene que enviar mucha información en XML. | Es más rápido, ya que usa binario y manda la información justamente necesaria. |
| Seguridad | Un cortafuegos podría dejar pasar información en XML. | Un cortafuegos podría denegar la entrada a información en binario. |
| Herramientas | Se puede usar WSDL para describir los servicios web que nos ofrece el servidor (se verá más adelante). |
2. Archivos de configuración
En los ejemplos anteriores, hemos visto como la configuración del servidor y del cliente la escribíamos dentro del programa (el modo, la dirección, el puerto). Ahora veremos una forma de tener nuestra configuración en un fichero separado.
Los archivos de configuración están escritos en XML. Ahora daremos los ejemplos de configuración del servidor y del cliente que podrían sernos útiles en los ejemplos anteriores.
2.1. Archivo para el servidor
El archivo que deberá recoger el servidor debe tener una estructura similar a esta:
Archivo: Servidor.exe.config
- <configuration>
- <system.runtime.remoting>
- <application>
- <!– Clase que vamos a compartir –>
- <service>
- <wellknown
- mode=“Singleton”
- type=“Calculo.Calculadora, Calculo”
- objectUri=“Calculadora.remota” />
- </service>
- <!– Canales que vamos a utilizar –>
- <channels>
- <channel ref=“http” port=“1234″ />
- <!– tambien podia haber sido tcp –>
- </channels>
- </application>
- </system.runtime.remoting>
- </configuration>
Como vemos, es muy simple e intuitivo. Los elementos que más nos interesan son wellknown y channel. Veamos los valores posibles en los atributos del elemento wellknown:
- mode: <Singleton> | <SingleCall> Con mode le indicamos la forma de compartir nuestra clase.
- type: <[namespace].[clase], [fichero]> Con type le indicamos es espacio de nombre, la clase que queremos compartir y en que archivo está.
- objectUri: <nombreQueLeDamosAlServicio> Con objectUri le ponemos un nombre al servicio.
Nota: el uso de los corchetes es para indicar que en ese lugar la escritura es libre, es decir, no tiene valores predeterminados.
Los atributos de los elementos channel podrán tener los siguientes valores:
- ref: <http> | <tcp> Con ref elegimos el canal.
- port: <numeroDelPuerto> Con port elegimos el puerto.
2.2. Archivo para el cliente
El archivo del cliente es aún más fácil:
Archivo: Cliente.exe.config
- <configuration>
- <system.runtime.remoting>
- <application>
- <client>
- <wellknown
- type=“Calculo.Calculadora, Calculo”
- url=“http://localhost:1234/Calculadora.remota” />
- </client>
- </application>
- </system.runtime.remoting>
- </configuration>
Como vemos, en el único elemento que nos debemos fijar es en wellknown, en el que sus atributos pueden coger los siguientes valores:
- type: <[namespace].[clase], [fichero]> Al igual que antes, la clase que queremos compartir.
- url: (<http> | <tcp>)://<host>:<puerto>/<nombreQueLeHanDadoAlServicio> La dirección dónde se está compartiendo la clase.
2.3. Los códigos del servidor y del cliente.
El código del servidor se nos reducirá considerablemente:
- using System;
- using System.Runtime.Remoting;
- using Calculo;
- public class Servidor
- {
- public static void Main(string [] args)
- {
- // Recogiendo el archivo de configuración del servidor.
- RemotingConfiguration.Configure(“Servidor.exe.config”);
- Console.WriteLine(“Atendiendo a las peticiones.”);
- Console.WriteLine(“Pulse ENTER para salir.”);
- Console.ReadLine();
- }
- }
El código del cliente es igual de fácil que el del servidor (de hecho usan la misma llamada).
- using System;
- using System.Runtime.Remoting;
- using Calculo;
- public class Cliente
- {
- public static void Main (string [] args)
- {
- // Recogiendo el archivo de configuración del cliente.
- RemotingConfiguration.Configure(“Cliente.exe.config”);
- // Creando el objeto
- Console.WriteLine(“Creando la calculadora”);
- Calculadora calc = new Calculadora();
- // Hacer uso de la calculadora
- /* ….. */
- }
- }
Ahora os estaréis preguntando el por qué de no haber dicho esto antes y tener que haber estado modificando el código del cliente y del servidor todo el rato… bueno, hay que aprender todos los métodos.
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. « [Leyendo]
- Uso de interfaces para separar el código entre cliente y servidor.

January 2nd, 2008 at 10:18 am
¿Alguien sabe como se usarían dos canales en el mismo cliente? Imaginaos que tengo un cliente que usa servicios de dos servidores remoting difernetes.
El problema es que al registrar el segundo canal, tengo una excepcion. ¿Alguien sabe como se podria solucionar?
Gracias!
January 2nd, 2008 at 11:40 am
Hola,
Sí se puede arreglar, pero hay que poner algo nuevo que no he explicado todavía, jejeje.
Ahora mismo no me lo sé de memoria, lo tengo apuntado por ahí, dame un día y te lo pongo aquí, como comentario.
Saludos.
January 2nd, 2008 at 1:37 pm
Hola,
Pues no encontrado los apuntes dónde tenía, así que he pensado que sería porque yo no encontraría ningún problema.
De todas formas, he creado un proyecto con un cliente que usa los servicios de dos servidores. Para comprobar que no hay “truco”, el cliente usa una interfaz de los servicios (lee el siguiente manual), así no usa directamente las clases y vemos que las tiene que usar remotamente.
http://eridem.net/wordpress/wp-content/uploads/2008/01/eridem_remoting_extra_02.zip
El proyecto está en Monodevelop. Espero que te sirva, y que encuentres la solución en este sencillo ejemplo.
Saludos.
April 11th, 2008 at 12:35 pm
[...] http://eridem.net/programacion-con-c-y-net-remoting-mono-remoting-vi/ [...]
November 3rd, 2008 at 6:20 pm
Hola, Estoy implementando un programa que requiere leer un archivo en el cliente y escribirlo en el servidor pero cuando lo hago saca una excepcion de seguridad que dice:
+ ex {“Por restricciones de seguridad, no se puede obtener acceso al tipo System.Runtime.Remoting.ObjRef.”} System.Exception {System.Runtime.Serialization.SerializationException}
alguien me puede ayudar para que se pueda hacer esto?
November 3rd, 2008 at 7:03 pm
Haz que los datos del archivo se conviertan en un array de bytes, y estos datos se los pasas al servidor que será el encargado de construir el archivo. Por ejemplo:
Servidor:
public void ConstruyeArchivo(byte[] data, string nombreArchivo)
{
// Construye el archivo mediante el array de bytes
}
Cliente:
public void EnviaArchivo(string nombreDelArchivo)
{
File archivo = new File(nombreDelArchivo);
byte[] data = FicheroAByte(archivo);
this.objetoRemoto.EnviaArchivo(data, archivo.Name);
}
Si necesitas más información sobre el archivo, pon más argumento (además del nombreDelArchivo, o si no necesitas nada, deja solo el array de bytes y deja al servidor que le ponga un nombre cualquiera (o que tu desees).
Saludos.
Ahh! Se me olvidaba, si lo que quieres no es construir el archivo en el servidor, sino leer el contenido (en texto) y enviarlo al servidor, haz lo mismo que anteriormente, pero recoge el contenido en un string y mándaselo al servidor.
November 5th, 2008 at 5:10 am
Buenas Noches, Muchas Gracias antes estaba haciendo algo parecido pero estaba pasando el FileStream y lo estaba armando en el servidor pero al leerlo en el cliente y pasar el byte[] y recontruirlo en el server no hubo problemas de seguridad.