Compartir una Instancia UdpClient en Aplicaciones .NET

Cuando se gestionan múltiples tipos de dispositivos, inicialmente podría requerirse un socket UDP distinto por tipo. Sin embargo, al aumentar la variedad de dispositivos, las rsetricciones de red pueden exigir un único punto de conexión compartido, identificando dispositivos mediante contenido de paquetes o direcciones IP.

class Principal
{
    static void Ejecutar(string[] args)
    {
        // Intento fallido de múltiples clientes
        UdpClient clienteA = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000));
        UdpClient clienteB = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000));
    }
}

El código anteriro genera una excepción SocketException ya que el socket solo puede abrirse una vez. Aplicar el patrón Singleton resuelve el problema de instancia única pero introduce limitaciones en la recepción de paquetes:

class Principal
{
    // Singleton
    private static UdpClient _instanciaUdp = null;
    
    public static UdpClient InstanciaUdp
    {
        get
        {
            if (_instanciaUdp == null)
            {
                _instanciaUdp = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000));
            }
            return _instanciaUdp;
        }
    }

    static void Ejecutar(string[] args)
    {
        UdpClient receptor1 = Principal.InstanciaUdp;
        UdpClient receptor2 = Principal.InstanciaUdp;
        
        UdpClient emisor = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6000));
        emisor.Send(new byte[] { 90 }, 1, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000));
        
        IPEndPoint remoto = new IPEndPoint(IPAddress.Any, 0);
        byte[] datos1 = receptor1.Receive(ref remoto); // Recibe paquete
        byte[] datos2 = receptor2.Receive(ref remoto); // Timeout o bloqueo
    }
}

Aquí, el segundo receptor no obtiene el paquete porque el Singleton no comparte datos recibidos. Para resolverlo, implementamos MutualUdpClient mediante:

  1. Singleton Pool: Gestiona el ciclo de vida compartido del socket UDP
  2. RouteUdpClient: Hilo independiente que captura paquetes y los redistribuye mediante eventos
  3. Cola interna: Cada MutualUdpClient almcaena paquetes recibidos para su consumo mediante Receive()

La solución permite múltiples instancias compartiendo un mismo endpoint:

class Principal
{
    static void Ejecutar(string[] args)
    {
        MutualUdpClient clienteX = new MutualUdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000));
        MutualUdpClient clienteY = new MutualUdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000));
        
        UdpClient transmisor = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6000));
        transmisor.Send(new byte[] { 90 }, 1, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000));
        
        IPEndPoint origen = new IPEndPoint(IPAddress.Any, 0);
        byte[] pkt1 = clienteX.Receive(ref origen); // Paquete recibido
        byte[] pkt2 = clienteY.Receive(ref origen); // Mismo paquete recibido
    }
}

Este enfoque mantiene la interfaz familiar de UdpClient mientras habilita compartición de conexión y distribución de paquetes.

Etiquetas: .NET UDP C# SingletonPattern SocketProgramming

Publicado el 7-15 03:03