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.