Datapayasadas

abril 9, 2010

Atacando dos frentes: Serialización de objetos y transporte sobre UDP

Filed under: .Net, C#, Desarrollo, Serialización, UDP — Etiquetas: , , — Matías @ 10:10 pm

Hola!, hoy voy a contarles un poco sobre el protocolo UDP y luego mostraré un ejemplo para aprovechar la serialización de objetos de la que les hablé hace unos días.

Como siempre, vamos a empezar por lo más básico.

¿Qué es UDP?

Es un protocolo a nivel de transporte (capa 4 del modelo OSI) basado en el intercambio de datagramas. Se puede decir que no es un protocolo orientado a la conexión, a diferencia del otro hiper-archi-conocido protocolo TCP que si lo es. Las siglas vienen del inglés: User Datagram Protocol.

Lo bueno de este protocolo, que también trae ciertas desventajas, es que proporciona una forma simple para que las aplicaciones envíen datagramas IP encapsulados sin tener que establecer una conexión. Si, leyeron bien, no hace falta establecer una conexión, cada datagrama tiene información suficiente para llegar a destino. UDP transmite segmentos que consisten en un encabezado seguido por la carga útil; cuenta además (aunque es opcional) con un checksum o suma de verificación, esto permite comprobar la integridad de los datos transferidos.

Todo esta muy lindo hasta acá, pero ahora les voy a tener que pinchar el globo.

Algunas desventajas del protocolo UDP

  • Volviendo al título elegido para este artículo podríamos decir que, al igual que en las batallas no todo lo que enviamos necesariamente llegara a destino. UDP no cuenta con un mecanismo de control de envíos, por lo que si no hacemos nada por nuestra cuenta podemos perder paquetes y nada nos va a alertar de ello, ni tampoco tratará de reenviarlos.
  • UDP también carece de control de flujo, o mejor dicho, no nos asegura que el orden en que enviamos los paquetes va a ser el mismo en el que se recibirán.

Volviendo al tema de programación, .Net nos ofrece la opción de trabajar con Sockets directamente o a través de clases de ayuda (que simplifican bastante el trabajo), como ser UdpClient.

Voy a mostrarles utilizando ejemplos de código dos pequeños programas, uno se encarga solamente de enviar y el otro solamente de recibir. Es la forma más básica que encontré y creo que así va a ser más fácil verlo (el emisor y receptor podrían estar en el mismo programa).

El código

Vamos a usar una clase serializable a la que llamaremos Paquete. Instancias de este objeto es lo que vamos a enviar y recibir sobre el protocolo UDP.

Paquete.cs

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
public class Paquete
{
    public Int32 Orden { get; set; }
    public String Mensaje { get; set; }

    public static byte[] Serializar(Paquete paquete)
    {
        using (MemoryStream memStream = new MemoryStream())
        {
            BinaryFormatter binFormatter = new BinaryFormatter();
            binFormatter.Serialize(memStream, paquete);

            return memStream.ToArray();
        }
    }

    public static Paquete Deserializar(byte[] paqueteEnBytes)
    {
        try
        {
            using (MemoryStream memStream = new MemoryStream(paqueteEnBytes))
            {
                BinaryFormatter binFormatter = new BinaryFormatter();
                memStream.Position = 0;

                return (Paquete)binFormatter.Deserialize(memStream);
            }
        }
        catch
        {
            return null;
        }
    }
}

Hasta acá no hay nada nuevo. Tenemos una clase con dos propiedades públicas, una de las cuales es un entero (orden) y la otra una cadena de caracteres (mensaje). También tenemos dos métodos estático (asi podemos llamarlos sin necesidad de referenciar una instancia de la clase) para serializar y deserializar.

Marcamos la clase como [Serializable] para que la podamos convertir a un arreglo de bytes con los métodos que mencione anteriormente. Vale la pena aclarar que todos los métodos que manejan sockets en .net, envian y reciben arreglos de bytes, por eso es tan importante la serialización en esto.

Ahora veamos el código para enviar un paquete:

EmisorUDP.cs

using System;
using System.Net;
using System.Net.Sockets;

class EmisorUDP
{
    static void Main(string[] args)
    {
        using (UdpClient cliente = new UdpClient())
        {
            byte[] bytesParaEnviar;
            String strPaquete = String.Empty;
            int contador = 0;

            while (true)
            {
                Console.Write("Escribe un mensaje y luego presiona [enter]: ");
                strPaquete = Console.ReadLine();

                if (String.IsNullOrEmpty(strPaquete))
                    return;

                Paquete paquete = new Paquete()
                {
                    Orden = contador,
                    Mensaje = strPaquete
                };

                bytesParaEnviar = Paquete.Serializar(paquete);
                cliente.Send(bytesParaEnviar, bytesParaEnviar.Length, new IPEndPoint(IPAddress.Broadcast, 9998));

                contador++;
            }
        }
    }
}

Bastante descriptivo en si, pero vamos a explicar un poco. Dentro de un ciclo while pedimos al usuario que ingrese un mensaje para enviar, si no escribe nada y solo presiona la tecla enter, entonces salimos del programa.

Si el usuario escribió algo lo guardamos en una nueva instancia de Paquete y además asignamos el número del mensaje (tomado desde el contador). Para poder enviar este paquete vamos a tener que, primero, obtener la representación en bytes y, segundo, especificar un destino. Para cumplir con lo primero serializamos la instancia que acabamos de crear; para lo segundo vamos a optar por enviar un mensaje del tipo broadcast en el puerto 9998, es decir, es un paquete que se va a enviar a todos los dispositivos de nuestra red.

Como ya dije antes, este proceso se va a repetir hasta que no se ingrese ningún caracter más, solo la tecla enter.

ReceptorUDP.cs

using System;
using System.Net;
using System.Net.Sockets;

class ReceptorUDP
{
    static void Main(string[] args)
    {
        using (UdpClient cliente = new UdpClient(9998))
        {
            while (true)
            {
                IPEndPoint emisor = null;
                byte[] bytesRecibidos = cliente.Receive(ref emisor);
                Paquete paquete = Paquete.Deserializar(bytesRecibidos);

                Console.WriteLine("Paquete enviado por {0}:", emisor);
                Console.WriteLine("-- Orden: {0}", paquete.Orden);
                Console.WriteLine("-- Mensaje: {0}", paquete.Mensaje);
            }
        }
    }
}

En el receptor tenemos que registrar el puerto en el que vamos a escuchar, en este caso es el 9998 (el que usamos al enviar), tarea que podemos realizar pasando el número de puerto en el constructor de UdpClient.

Notese que necesitaremos crear un objeto de la clase IPEndPoint para pasarlo por referencia, como parametro, al método Receive de UdpClient. Éste último se encargara de guardar ahí la información del emisor (como ser la dirección IP y el puerto remoto).

Otra cosa importante, tanto UdpClient.Send como UdpClient.Receive son métodos sincronicos, es decir, se van a bloquear hasta que envíen o reciban un paquete. Cuando empiecen a trabajar “en serio” con UDP (o TCP) van a tener que usar Threads (Hilos) independientes para no congelar la interfaz del usuario o probar con los métodos asíncronos tales como UdpClient.BeginSend, UdpClient.BeginReceive.

Volviendo al código de ejemplo, una vez que recibimos un paquete (en realidad, la representación en un arreglo de bytes) vamos a poder deserializarlo para finalmente acceder a las propiedades Orden y Mensaje; imprimimos en pantalla esos datos y esperamos el siguiente paquete.

Recuerden las desventajas de las que hablamos, UDP no nos asegura que todo lo enviado sea recibido, ni que esten en el orden correcto.

Si todo ha salido bien, deberían ver algo así:

Emisor y receptor UDP

Bueno, esto es todo por ahora, en el próximo artículo vamos a hacer lo mismo pero utilizando el protocolo TCP.

Anuncios

1 comentario »

  1. Excelente aporte, muchas gracias

    Comentario por Alejandro muñoz — agosto 2, 2013 @ 6:52 pm


RSS feed for comments on this post. TrackBack URI

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Crea un blog o un sitio web gratuitos con WordPress.com.

A %d blogueros les gusta esto: