Come un Client UDP Invia Datagrammi: Flusso bind(), sendto(), recvfrom()
Ciclo di Vita del Client UDP (Socket POSIX)
A differenza del TCP, l'UDP è senza connessione. Il client deve esplicitamente associare una porta locale (opzionale ma consigliato) e specificare la destinazione a ogni invio.
Sequenza Passo dopo Passo delle Chiamate di Sistema
1. Crea Socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) perror("socket");
2. Associa Porta Locale (Opzionale ma Consigliato)
struct sockaddr_in local = {0}; local.sin_family = AF_INET; local.sin_addr.s_addr = htonl(INADDR_ANY); local.sin_port = htons(0); // 0 = l'OS assegna una porta effimera bind(sockfd, (struct sockaddr*)&local, sizeof(local));
L'associazione permette al server di rispondere a una porta nota e abilita le regole del firewall.
3. Risolvi l'Indirizzo del Server
struct addrinfo hints = {0}, *res; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; getaddrinfo("server.example.com", "5000", &hints, &res); // res->ai_addr ora contiene il sockaddr_in di destinazione
4. Invia Datagramma
char *msg = "Hello UDP"; sendto(sockfd, msg, strlen(msg), 0, res->ai_addr, res->ai_addrlen);
Argomenti di sendto: socket, buffer, lunghezza, flag (0), destinazione sockaddr, lunghezza sockaddr.
5. Ricevi Risposta (Opzionale)
char buf[1500]; struct sockaddr_in from; socklen_t fromlen = sizeof(from); ssize_t n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen); if (n > 0) process(buf, n, &from);
recvfrom si blocca fino all'arrivo di un datagramma. from contiene l'IP/porta del mittente.
6. Pulizia
freeaddrinfo(res); close(sockfd);
Differenze Chiave dal TCP
| Aspetto | TCP | UDP |
|---|---|---|
| Connessione | connect() stabilisce | Nessuna connessione; sendto specifica la destinazione ogni volta |
| Affidabilità | Garantita, ordinata | Best-effort; può perdere, riordinare, duplicare |
| Controllo di Flusso | Sì (finestra) | No (l'app deve regolare il ritmo) |
| Uso Tipico | Web, email, SSH | DNS, NTP, VoIP, gaming, streaming, IoT |
Insidie Comuni
- Dimenticare
bind()→ il server risponde a una porta effimera; NAT/firewall potrebbe scartare. - Non controllare i valori di ritorno di
sendto/recvfrom. - Assumere che i confini del datagramma siano preservati (lo sono, ma dimensione limitata a ~64 KB, MTU pratica ~1500).
recvfrombloccante per sempre — usaselect()/poll()osetsockopt(SO_RCVTIMEO)per i timeout.