Mr UDP, Mr TCP e la fotografia abbagliante di un capolavoro |
...It implements a connectionless, unreliable datagram packet service. Packets may be reordered or duplicated before they arrive. UDP generates and checks checksums to catch transmission errors...Non facciamoci tradire dalla (preoccupante) parola unreliable: se lo usiamo nelle applicazioni giuste (quelle dove la perdita/duplicazione di un pacchetto non è un errore tragico... il VoIP, per esempio) le doti di UDP (velocità, leggerezza, facilità di implementazione) vengono fuori alla grande.
Per cui vi propongo un UDP Server che è quasi una copia del TCP server visto qui, e che già a prima vista si nota più semplice. Vai col codice!
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <errno.h> #define MYBUFSIZE 1024 int main(int argc, char *argv[]) { // test argomenti if (argc != 2) { // errore args printf("%s: numero argomenti errato\n", argv[0]); printf("uso: %s port [i.e.: %s 8888]\n", argv[0], argv[0]); return EXIT_FAILURE; } // crea un socket int my_socket; if ((my_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { // errore socket() printf("%s: non posso creare il socket (%s)\n", argv[0], strerror(errno)); return EXIT_FAILURE; } // prepara la struttura sockaddr_in per questo server struct sockaddr_in server; // (local) server socket info memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; // set address family server.sin_addr.s_addr = INADDR_ANY; // set server address for any interface server.sin_port = htons(atoi(argv[1])); // set server port number // bind informazioni del server al socket if (bind(my_socket, (struct sockaddr *)&server, sizeof(server)) < 0) { // errore bind() printf("%s: errore bind (%s)", argv[0], strerror(errno)); return EXIT_FAILURE; } // loop di ricezione messaggi dal client struct sockaddr_in client; // appoggio per dati del client per poter rispondere socklen_t socksize = sizeof(struct sockaddr_in); char client_msg[MYBUFSIZE]; while (recvfrom(my_socket, client_msg, MYBUFSIZE, 0, (struct sockaddr *)&client, &socksize) > 0) { // send messaggio di ritorno al client printf("%s: ricevuto messaggio dal sock %d: %s\n", argv[0], my_socket, client_msg); char server_msg[MYBUFSIZE]; sprintf(server_msg, "mi hai scritto: %s", client_msg); if (sendto(my_socket, server_msg, strlen(server_msg), 0, (struct sockaddr *)&client, sizeof(client)) < 0) { // errore send() printf("%s: errore send (%s)\n", argv[0], strerror(errno)); return EXIT_FAILURE; } // clear buffer memset(client_msg, 0, MYBUFSIZE); } // errore recv(): il "client disconnected" con recv_size==0 non esiste perché questo server è connectionless printf("%s: errore recv (%s)\n", argv[0], strerror(errno)); return EXIT_FAILURE; }Ok, come vedete è ampiamente commentato e quindi è auto-esplicativo, per cui non mi dilungherò sulle singole istruzioni e/o gruppi di istruzioni (leggete i commenti! sono li per quello!), ma aggiungerò, solo, qualche dettaglio strutturale.
La struttura è quella classica e basica di un UDP Server:
- socket() - crea un socket
- prepara la struttura sockaddr_in per questo server
- bind() - bind informazioni del server al socket
- recvfrom() - loop di ricezione messaggi dal client
Come (spero) avrete notato, invece delle funzioni send/recv si usano sendto/recvfrom (che sono identiche alle controparti TCP ma aggiungono i dati dei sender/receiver) e, inoltre, mancano le fasi di listen e accept, il che rende il Server più semplice da scrivere e anche da usare: si possono fare sequenze di start/stop/restart di Server e Client nell'ordine che si vuole e la comunicazione, magicamente, si restaurerà sempre, mentre la versione TCP (immagino l'abbiate testata, e se no: correte a farlo!) che ha una fase di connessione, necessita di una più rigorosa sequenza di start/stop per funzionare.
Ci rivediamo per il UDP Client e, come sempre, non trattenete il respiro nell'attesa...
Ciao e al prossimo post!
Scusa ma come faccio a passare una struct con un server UDP?
RispondiEliminaCiao "Unknown", il buffer che si passa alla recvfrom() (e, specularmente, alla sendto()) è un void* quindi puoi passargli quello che vuoi: nell'esempio qui sopra è un array di chars, ma puoi passargli benissimo un pointer a una struttura dati. Spero di esserti stato utile.
Elimina