Comunicazione UDP con UdpClient in C#

Comunicazione UDP con UdpClient in C#

Nell'articolo precedente abbiamo usato TCP, il protocollo affidabile e orientato alla connessione. Passiamo ora al suo complemento: UDP, un protocollo senza connessione che privilegia velocità e leggerezza a scapito delle garanzie di consegna. UDP invia pacchetti indipendenti, chiamati datagrammi, senza stabilire una connessione né verificare che arrivino a destinazione. In .NET, la classe UdpClient semplifica il suo utilizzo. In questo articolo vediamo come inviare e ricevere datagrammi e quando UDP è la scelta giusta.

La natura di UDP

La differenza fondamentale rispetto a TCP è l'assenza di connessione. Con UDP non esiste una fase di negoziazione iniziale né un canale persistente: ogni datagramma è un'entità autonoma, spedita e dimenticata. Il protocollo non garantisce che il pacchetto arrivi, né che arrivi una sola volta, né nell'ordine di invio. In cambio, elimina il sovraccarico del mantenimento della connessione e del controllo di affidabilità, risultando più rapido e adatto agli scenari in cui la tempestività conta più della completezza.

Ricevere datagrammi

Un ricevitore UDP crea un UdpClient legato a una porta e attende i datagrammi in arrivo. Il metodo ReceiveAsync restituisce un risultato che contiene sia i dati sia l'endpoint del mittente, informazione preziosa poiché, in assenza di connessione, è l'unico modo per sapere da dove proviene ciascun pacchetto e per rispondere.

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

// Il ricevitore ascolta sulla porta 6000
using var receiver = new UdpClient(6000);
Console.WriteLine("In ascolto di datagrammi sulla porta 6000");

while (true)
{
    // Attende un datagramma; il risultato include il mittente
    UdpReceiveResult result = await receiver.ReceiveAsync();

    string message = Encoding.UTF8.GetString(result.Buffer);
    Console.WriteLine($"Da {result.RemoteEndPoint}: {message}");
}

Inviare datagrammi

Il mittente crea a sua volta un UdpClient e invia i byte del messaggio a un indirizzo e a una porta con SendAsync. Non essendoci connessione da stabilire, l'invio è immediato: non c'è alcuna garanzia né conferma che il destinatario abbia ricevuto il pacchetto.

using var sender = new UdpClient();

byte[] data = Encoding.UTF8.GetBytes("Aggiornamento posizione");

// Invia il datagramma; nessuna conferma di consegna
await sender.SendAsync(data, data.Length, "127.0.0.1", 6000);
Console.WriteLine("Datagramma inviato");

Connettere un UdpClient

Anche se UDP è senza connessione, UdpClient offre un metodo Connect che fissa un endpoint remoto predefinito. Non stabilisce una vera connessione, ma consente di inviare senza ripetere ogni volta indirizzo e porta e filtra i datagrammi in arrivo accettando solo quelli da quell'endpoint. È una comodità quando si comunica sempre con lo stesso interlocutore.

using var client = new UdpClient();

// Fissa l'endpoint remoto predefinito
client.Connect("127.0.0.1", 6000);

byte[] data = Encoding.UTF8.GetBytes("Heartbeat");

// Non serve piu specificare la destinazione
await client.SendAsync(data);

Il broadcast e il multicast

UDP abilita modalità di comunicazione impossibili con TCP. Il broadcast invia un datagramma a tutti i dispositivi della rete locale, mentre il multicast lo recapita a un gruppo di interessati che si sono iscritti a uno specifico indirizzo. Sono tecniche usate per la scoperta di servizi sulla rete e per la distribuzione efficiente di dati a molti destinatari.

using var client = new UdpClient();
client.EnableBroadcast = true;

byte[] announcement = Encoding.UTF8.GetBytes("Servizio disponibile");

// L'indirizzo di broadcast raggiunge tutti gli host della sottorete
var broadcastEndpoint = new IPEndPoint(IPAddress.Broadcast, 6000);
await client.SendAsync(announcement, announcement.Length, broadcastEndpoint);

Gestire l'inaffidabilità

Poiché UDP non garantisce la consegna, le applicazioni che ne hanno bisogno devono implementare da sé le contromisure necessarie. A seconda dei requisiti, questo può significare numerare i pacchetti per rilevare quelli persi o fuori ordine, ritrasmettere i dati critici, oppure semplicemente accettare la perdita quando un dato obsoleto è irrilevante. Quest'ultimo è spesso il caso: in uno streaming video o in un gioco, un fotogramma o una posizione scaduti non vanno recuperati, perché nel frattempo ne sono già arrivati di più recenti.

Quando scegliere UDP

La scelta tra TCP e UDP dipende dalla priorità dell'applicazione. UDP è indicato quando la velocità e la bassa latenza contano più della consegna garantita, e quando la perdita occasionale di un pacchetto è tollerabile. Gli scenari tipici includono:

  • Streaming audio e video in tempo reale, dove la fluidità prevale sulla perfezione.
  • Giochi online, in cui la reattività è essenziale e i dati invecchiano rapidamente.
  • Telemetria e sensori che inviano aggiornamenti frequenti, dove un singolo valore perso è irrilevante.
  • Scoperta di servizi e protocolli come DNS, che usano scambi rapidi di richiesta e risposta.

Per tutto ciò che richiede integrità e ordine, come il trasferimento di file o le transazioni, TCP resta la scelta obbligata.

Conclusione

Abbiamo esplorato UDP come protocollo senza connessione, inviando e ricevendo datagrammi con UdpClient e ottenendo l'endpoint del mittente in ricezione. Abbiamo visto la comodità di Connect, le modalità broadcast e multicast, e la necessità di gestire manualmente l'inaffidabilità quando serve. Infine abbiamo delineato gli scenari in cui UDP è preferibile a TCP. Nel prossimo articolo torneremo a una comunicazione affidabile ma in tempo reale, con i WebSocket.