Friday, February 02, 2007

Aprendiendo remoting con Mono

Hola,

Hace ya más de un mes que no publico nada por falta de tiempo. Pero ahora creo que voy hacer una pequeña importante contribución a la causa de aprender .Net, sobre todo usando mono.

En esta dirección podeis encontrar un fichero comprimido que contiene dos programas y que serán una de las bases de Palantir.Net. Se trata de un cliente y un servidor chat en modo texto, muy sencillos para poder aprender de ellos pero alta y facilmente ampliables, que hacen uso de las tecnicas de Remoting que incluye Mono. Intentaré explicar las clases que tiene cada programa y como funciona.

El Servidor


En el servidor podemos encontrar 2 clases principales y 3 espacios de nombres:

  • Clases:


    • MainInit: Esta clase será la que contiene el metodo main del servidor y sirve para inicializar el servidor chat y crear el objeto compartido Login.

    • MainServer: Esta clase mantendrá la información y el objeto Server.


  • Espacio de nombres:


    • DatafromDB: Unos cuantos ejemplos de como acceder a una base de datos MySql. Ahora están desabilitados pero en un futuro se usará para que el servidor almacene toda la información de estado en vivo del chat para que sea accesible por ejemplo mediante una interfaz web.

    • Objects: Contiene las clases que funcionan en "modo local", es decir, estas clases solo funcionan en el servidor y no pueden ser enviadas a los clientes.

    • RemoteObjects: Estas clases sí son MarshalByRefObject en su mayoria y heredan de Interfaces que están definidas en la seccion Common.




Funcionamiento del servidor

Cuando ejecutamos el servidor ocorren los siguientes pasos:

  1. Se ejecuta el MainInit que crea un MainServer que mantiene un objeto Server. Con esto podemos tener varios servidores ejecutandose al mismo tiempo. A continuación comparte por http un objeto Login:


    HttpChannel channel = new HttpChannel(8085);

    ChannelServices.RegisterChannel(channel);

    RemotingConfiguration.ApplicationName = "Server Conference";
    RemotingConfiguration.RegisterWellKnownServiceType(typeof(Login),"Login.soap",
    WellKnownObjectMode.Singleton);



  2. Cuando creamos un objeto Server este contendrá una lista de canales que hay disponibles en el servidor. Por defecto estará vacia.

  3. La clase Login será la encargada de gestionar si un cliente tiene acceso al servidor o no.

  4. Apartir de aquí el servidor se pondría en espera.



Supongamos que un cliente quiere conectarse:

  1. El cliente obtiene la clase Login y si consigue realizar con exito un loginUser puede solicitar un ServerHandler.

  2. Una vez que se tiene un ServerHandler se pueden solicitar canales y unirse a los mismos. Si solicitamos un findChannel obtendremos un ChannelHandler.

  3. Con este ChannelHandler podemos hacer un loginUser para unirnos al canal.



Diferentes aspectos técnicos a tener en cuenta:

  • Server mantiene una lista con todos los canales disponibles.




El proceso de envio recepción de un mensaje tiene lugar principalmente en las clases pertenecientes al servidor:

  1. El cliente ya tiene un objeto ChannelHandler de un canal determinado, para enviar un mensaje invoca el método sendMessageChannel(string message). En el servidor se ejecutará el siguiente código:



  2. public override void sendMessageChannel(string message)
    {
    MessageChannelArgs msgarg = new MessageChannelArgs(message,_idChannel,_user.Name);
    OnMessageChannel(this, msgarg);

    }


    Como podéis leer lo que ocurre es que se llama al evento OnMessageChannel. Conectado a este evento está el correspondiente Channel. Cuando se creo el ChannelHandler se produjo esta conexión.

  3. Al producirse OnMessageChannel se ejecuta el método sendMessageChannel de la clase Channel. Dentro de este método podemos escribir todo lo que queremos que se haga cuando ocurre un mensaje, por ejemplo hacer log del mismo.

  4. Dentro de este método se produce una llamada al evento OnReceiveMessageChannel(null, msgarg) al que todos los ChannelHandlers pertenecientes a ese Channel están conectados. Creo que este es un buen método para evitar hacer el uso de bucles for y al almacenamiento en listas de los ChannelHandler.

  5. Tras esto en los ChannelHandler se ejecuta el metodo receiveMessageChannel(object channel, MessageChannelArgs args) que en su interior contiene una llamada al evento OnReceiveMessageChannel al cual el UserHandler fue conectado cuando hizo loginUser para poder recibir el mensaje.

  6. A continuación en el objeto UserHandler se ejecutará receiveMessageChannel que llama al eventoOnReceiveMessageChannel(channel, msgarg) al cual el cliente deberá haber conectado un delegado para poder recibir finalmente los mensajes.


Posiblmente se podría haber hecho mejor, y por eso acepto todo tipo de sugerencias, pero creo que no es una mala forma de resolver el problema.

El Cliente


El funcionamiento del cliente es muy sencillo.

  1. Obtner el objeto login del servidor.



  2. HttpChannel channel = new HttpChannel();

    ChannelServices.RegisterChannel(channel);

    ILogin login = (ILogin)Activator.GetObject(
    typeof(ILogin),"http://localhost:8085/Login.soap");


  3. Creamos un objeto UserHandler con la información del usuario.

  4. Si el login tuvo exito obtendremos un ServerHandler y ya podemos pasar a crear canales y unirnos a ellos.

  5. Para obtner un canal llamamos al metodo findChannel(string idcanal) que nos devuelve el correspondiente ChannelHandler si la busqueda tuvo exito.

  6. En caso contrario podemos crear un nuevo canal:



  7. chn =(IChannelHandler) srvhandler.findChannel(message.Remove(0,6));

    //If it fails you can create the channel
    if(chn==null)
    {
    Console.WriteLine("Creating channel.");
    srvhandler.createChannel(message.Remove(0,5));
    //After createChannel you have to search the channel again.
    chn =(IChannelHandler) srvhandler.findChannel(message.Remove(0,6));
    }


  8. Tenemos que conectar un delegado al evento onReceiveMessage del UserHandler:



  9. usuario.OnReceiveMessageChannel += new UserHandler.MessageChannelHandler(DisplayMessage);



El código que se puede ver acontinación en el fichero del cliente es un simple manejador de comandos para hacer unas basicas tareas.

Network Multimedia Middleware

Hola,

Este articulo lo voy a escrbir en castellano pues información en ingles hay mucha en el propio sitio de NMM. Por la tanto esta será una introducción a estas fantasticas librerias multimedia y a sus principales programas.


¿Qué es NMM?



Principalmente son unas librerias que nos sirven para hacer cosas de multimedia: reproducir videos, ver la televisión, dvd, captura de sonído, conversión de formatos, etc. Bajo esta perspectiva simplemente parecerian unas librerias más pero tienen una particularidad muy importante: la red es transparente. Con esto quiero decir que da igual donde tengamos cada componente que forma parte de la tarea multimedia pues estas librerias se encargan de manajar la red y hacerla invisble de cara al usuario.

De lo más facil a lo complicado


La instalación


Por desgracia actualmente, o al menos eso creo, no existen rpm ni deb para las distribuciones actuales. Pero no todo está perdido, los desarrolladores (no he hablado de ellos más, adelante lo haré) han habilitado varios medios para poder instalar NMM.

Desde precompilado en tar.gz


Esta es la opción más sencilla. Primero tenemos que descargar la versión 0.7, hay dos ficheros:
  • Compilada con gcc version 2.95: aquí
  • Compilada con gcc version 3.X: aquí

Tambien podemos encontrar las nightly builds de Sourceforge aquí pero como siempre estas son version de test aunque suelen funcionar.
Ya tenemos el fichero acontinuación lo descomprimimos:

elessar@minastirith$ cd /home/elessar
elessar@minastirith:/home/elessar/$ tar zxvf nmm-0.7.0-binaries-gcc3-i686.tar.gz

Ahora definimos la variable de entorno que utilizará NMM para saber donde está instalado y indicamos el directorio de las librerias para el linker:

elessar@minastirith:/home/elessar$ export NMM_DEV_DIR=/home/elessar/nmm-0.7.0
elessar@minastirith:/home/elessar$
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/elessar/nmm-0.7.0/lib

Ahora tenemos que ejecutar el programa serverregistry para generar el fichero con los recursos multimedia que están disponibles en nuestro ordenador:

elessar@minastirith:/home/elessar$ cd nmm-0.7.0/bin
elessar@minastirith:/home/elessar/nmm-0.7.0/bin$ ./serverregistry -s

Y esperamos a que termine su ejecución. Ahora realizaremos una simple prueba para comprobar si funciona correctamente:

elessar@minastirith:/home/elessar/nmm-0.7.0/bin$ ./helloworld1 fichero.mp3

El programa helloworld1 es un programa de demostración y aprendizaje muy simple y cuyo código fuente puede encontrarse en las fuentes de NMM. Si todo va bien deberiamos escuchar el fichero.mp3. Y con esto ya podemos pasar a hacer cosas mucho más interesantes.

Desde el código fuente


El fichero es la versión 0.7 y puede encontrase aquí. En este caso tras la liberación del fichero sacaron un patch para poder usar las FFMpeg. La información sobre como aplicar el patch puede encontrarse aquí.

elessar@minastirith$ cd /home/elessar
elessar@minastirith:/home/elessar/$ tar zxvf nmm-0.7.0.tar.gz

Aunque podemos compilar NMM contra las librerias (recuerdo que hacer falta los paguetes -devel) que tengamos instaladas yo recomiendo no hacerlo pues NMM es bastante follonero con los numeros de versión y al final resulta mucho más facil y cómodo descargarse los dos siguientes ficheros:
nmm-0.7.0-external-libs.tar.gz
nmm-0.7.0-external-includes.tar.gz
Los descomprimimos:

elessar@minastirith:/home/elessar/$ tar zxvf nmm-0.7.0-external-libs.tar.gz
elessar@minastirith:/home/elessar/$ tar zxvf nmm-0.7.0-external-includes.tar.gz

Y si tenemos la versión del gcc 3.X:

elessar@minastirith:/home/elessar/$ cd nmm-0.7.0-external-libs
elessar@minastirith:/home/elessar/nmm-0.7.0-external-libs$ ./make_gcc3_libs

O si tenemos la version 2.95:

elessar@minastirith:/home/elessar/$ cd nmm-0.7.0-external-libs
elessar@minastirith:/home/elessar/nmm-0.7.0-external-libs$ make

Bien a continuación definimos las variables de entorno:

elessar@minastirith:/home/elessar/$ cd /home/elessar/nmm-0.7.0
elessar@minastirith:/home/elessar/nmm-0.7.0$ export NMM_DEV_DIR=/home/elessar/nmm-0.7.0
elessar@minastirith:/home/elessar/nmm-0.7.0$
export LD_LIBRARY_PATH=/home/elessar/nmm-0.7.0-external-libs:$LD_LIBRARY_PATH

Acontinuación pasamos a configurar y compilar:

elessar@minastirith:/home/elessar/nmm-0.7.0$
./configure --with-extra-libs=/home/elessar/nmm-0.7.0-external-libs --with-extra-includes=/home/elessar/nmm-0.7.0-external-includes
elessar@minastirith:/home/elessar/nmm-0.7.0$ make

Tras esto nos queda esperar pues tardará un rato en compilarlo todo. Una vez finalizada la compilación podemos realizar el mismo test de antes para comprobar que funciona correctamente.

Lo interesante: quiero multimedia distribuida


Vamos a dejarnos de vueltas y vamos a lo importante pues ya lo tenemos instalado. Hay 2 programas principales:

serverregistry


Este programa es el que se encarga de gestionar los recursos y realizar las conexiones oportunas entre las diferentes máquinas. Por ello tendremos que tener ejecutando en una consola diferente a la que usamos para ejecutar clic.

clic


Este programa es el que podriamos llamar el "reproductor". Tiene 2 maneras diferentes de funcionar:

  • Usando ficheros .gd: Asi tendremos toda la potencia de NMM

  • Usando Graph Builder: Para cosas más sencillas o típicas


Ficheros .gd


Tienen una sintaxis bastante sencilla que veremos a base de ejemplos.
Reproducir un fichero .mp3: mp3.gd

% This graph description realizes a simple MP3 player.
% Use the -i option of clic to specify the MP3 file

MP3ReadNode ! MPEGAudioDecodeNode ! PlaybackNode

Y para ejecutarlo:

elessar@minastirith:/home/elessar/nmm-0.7.0/bin/$
./clic mp3.gd -i /home/elessar/audio/song.mp3

Vosotros direis: Esto esta muy bien pero para hacer esto uso el amarok que está más chulo. Y teneis razón así que vamos a complicarlo un poquito. Supongamos que tenemos los ordenadores minastirith y minasmorgul (ambos deben estar nombrados en el fichero /etc/hosts o ser resolubles por dns ya que de momento las IP no están soportadas) y en ambos tengo instalado NMM y serverregistry se está ejecutando. Yo tengo el fichero en minastirith pero lo quiero escuchar en minasmorgul. Pues creamos el siguiente fichero: mp3remote.gd

% This graph description describes a simple MP3 player where the MPEGAudioDecodeNode
% is running on host "minasmorgul". The serverregistry on host "minasmorgul"
% is listening on port 22801.
% Use the -i option of clic to specify the MP3 file

MP3ReadNode !
MPEGAudioDecodeNode # setLocation("minasmorgul") !
PlaybackNode # setLocation("minasmorgul")

Y lo ejecutamos en minastirith con:

elessar@minastirith:/home/elessar/nmm-0.7.0/bin/$
./clic mp3remote.gd -i /home/elessar/audio/song.mp3

Y deberiamos escuchar el fichero en minasmorgul. Esto ahora si que está mucho más interesante.
Pero esto es un ejemplo muy sencillo podemos hacer mucho más. Supongamos que tenemos una lectora de dvd en minastirith y que minasmorgul no tiene. Pues podemos usar NMM para ver nuestros videos más facilmente pero esto lo explicaré usando Graph Builder.

Graph Builder


Aunque el ejemplo puesto en el apartado anterior era un simple reproductor de mp3 solo hay coger la documentación disponible para extender las capacidades mucho más allá. Pero para cosas más cotidianas la gente de NMM han habilitado un método más sencillo: los Graph Builder.
Reproductor DVD: Como hemos dicho anteriormente queremos ver el DVD en minasmorgul pero este no dispone de unidad lectora. Pues es así de sencillo:

elessar@minasmorgul:/home/reybrujo/nmm-0.7.0/bin/$
./clic -u dvd://minastirith/dev/dvd?title=1&chapter=4&angle=1"

Así de simple. La sintaxis completa de este comando es:

dvd://<hostname>/<path to dvd device>?title=<number>&
chapter=<number>&angle=<number>

Hay otros Graph Builder disponibles como pueden ser:

file://<hostname>/<path and filename>
audiocd://<path to audio cdrom device>?track=<track number>
tv://<hostname>/<path to video device>
dvbtv://<hostname>/<path to video device>
ivtv://<hostname>/<path to video device>
mpegtv://<hostname>/<path to video device>

Podeis encontrar más información aquí.

Terminando


Para terminar esta introducción diré varias cosas que puede en entradas posteriores amplie. Este programa lo está desarrollando principalmente un grupo de personas de la universidad alemana Computer Graphics Lab - University of Saarbrücken. Son gente maja y estan haciendo un gran software. Yo por mi parte he realizado un plugin para captura de video usando V4L2 y que puede encontrarse en el CVS en la sección contrib (esto es para subirme un poco el ego ;-) ).

Tienen tambien desarrollado un plugin para poder usar NMM con Amarok.

La sección de documentación es bastante buena y en ella podemos encontrar un listado con los plugins que tiene y una breve descripción de para que sirven.

Bueno quizas me dejo algo en el tintero pero ya hablaremos más de NMM más tarde.

Un saludo.