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.

No comments: