Sue: Hai sentito che il film di Philippe ha vinto il primo premio al Festival di Colonia, oggi?.
Mort: Molto eccitante. Ma Eichmann non era di lì?
E va bene, questa volta ci siamo, è il turno della seconda parte di Rifkin's Festival (oops: ZeroMQ Festival), come promesso nella prima parte dell'articolo. Qui sopra vi ho proposto un commento secco e lapidario di Mort (Wallace Shawn), il protagonista alter-ego di Woody Allen in questo bel film, un commento che ben introduce questo articolo: dopo aver parlato dell'interfaccia a ZeroMQ di "basso livello", e cioè la libreria libzmq, è il momento di parlare dell'interfaccia ad "alto livello", la CZMQ (libczmq). Grazie a CZMQ è possibile scrivere codice di rete sintetico e compatto (anzi, secco e lapidario) anche per problemi complessi, problemi che richiederebbero la scrittura di codice lungo e complicato anche aiutandosi con ZeroMQ low-level (e lunghissimo e complicatissimo usando, ad esempio, i classici BSD Socket).
...crepi l'avarizia, oggi ti offrirò un menu high-level... |
Allora: CZMQ è una interfaccia C ma è strutturata come se fosse una libreria di classi (nel manuale il termine "class" viene usato frequentemente). Comunque niente paura per chi non è avvezzo al C++: è una interfaccia C al 100%. E questa è la descrizione che danno gli autori stessi della libreria nel manuale:
CZMQ ha questi obiettivi:
- "wrappare" l'API core di ØMQ in una semantica che sia naturale e porti ad applicazioni più brevi e leggibili.
- Nascondere le differenze tra le versioni di ØMQ.
- Fornire uno spazio per lo sviluppo di semantiche API più sofisticate.
E quali sono, in pratica, i vantaggi dell'uso di CZMQ? Facciamo un caso reale: se, per esempio, volessimo risolvere un problema complicato come scrivere un Load Balancing Broker che segua lo schema di funzionamento seguente (copio una descrizione di questo oggetto da 0MQ - The Guide):
grazie a CZMQ potremmo farlo in maniera abbastanza sintetica e leggibile. Mentre se provassimo a farlo con altri strumenti ci renderemmo subito conto della grande differenza a livello di lunghezza e complicazione. Per quanto riguarda la realizzazione pratica di questo LBB (visto che non amo fare il copia-incolla di cose non mie) vi passo direttamente due link dove viene descritto il codice corrispondente, un doppio codice scritto usando le due librerie (low e high-level): se gli date un occhiata noterete che con CZMQ è decisamente più semplificato e leggibile. Ah, e vi consiglio un utile esercizio: provate a riscrivere (o, perlomeno, a immaginare) lo stesso codice scritto con i BSD Socket... tremo solo a pensarci. Ecco i link: versione con lzmq e versione con CZMQ.
E adesso? Ma non posso chiudere un articolo senza proporre nessun codice mio! Quindi ho pensato che per ben descrivere la differenza di approccio tra low-level e high-level è meglio partire dalle basi, e allora vi propongo un esempio semplicissimo di codice Client/Server scritto in tre versioni, BSD Socket, ZeroMQ low-level e CZMQ.
Ho scritto tutti gli esempi a mo' di codice di test (e non di produzione), quindi sono (ahimè) completamente assenti le importantissime istruzioni di trattamento degli errori (praticamente sono gli scheletri funzionali da completare per entrare in produzione): questo con l'obiettivo di non sviare l'attenzione dal flusso base del codice, quello che ci mostra come si lavora con una libreria rispetto a un'altra. Tutti gli esempi sono composti da due file, client.c e server.c, e sono perfettamente compilabili e funzionanti. Ok, cominciamo con i BSD Socket, vai col codice!
// server.c - un semplice server con BSD Socket#include <stdio.h>#include <string.h>#include <unistd.h>#include <arpa/inet.h>// main() - funzione mainint main(void){// creo il socket TCPprintf ("Avvio server...\n");int srv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// preparo la struttura sockaddr_in per questo serverstruct sockaddr_in server;memset(&server, 0, sizeof(struct sockaddr_in));server.sin_family = AF_INET; // set famiglia di indirizziserver.sin_addr.s_addr = INADDR_ANY; // set indirizzo del serverserver.sin_port = htons(5555); // set port del server// associo l'indirizzo del server al socket e start ascoltobind(srv_sock, (struct sockaddr *)&server, sizeof(server));listen(srv_sock, 10);// accetto connessioni da un client entrantesocklen_t socksize = sizeof(struct sockaddr_in);struct sockaddr_in client; // struttura sockaddr_in per il client remotoint cli_sock = accept(srv_sock, (struct sockaddr *)&client, &socksize);close(srv_sock);// loop di ricezionechar string[10];while (recv(cli_sock, string, sizeof(string), 0) != -1) {printf("richiesta ricevuta: %s\n", string);// invio la rispostasend(cli_sock, "Pong", 5, 0);}// disconnettoclose(cli_sock);return 0;}
E passiamo ora al codice in versione libzmq (ZeroMQ low-level):
// client.c - un semplice client ZeroMQ con libzmq#include <stdio.h>#include <zmq.h>// main() - funzione mainint main(void){// creo il context e il requesterprintf ("Connessione al server...\n");void *context = zmq_ctx_new();void *requester = zmq_socket(context, ZMQ_REQ);// connetto il requester al responderzmq_connect(requester, "tcp://localhost:5555");// loop di invioint cnt = 0;while (cnt++ < 10) {// invio la richiestazmq_send(requester, "Ping", 5, 0);// ricevo la rispostachar string[10];zmq_recv(requester, string, sizeof(string), 0);printf("risposta ricevuta: %s\n", string);}// disconnettozmq_close (requester);zmq_ctx_destroy (context);return 0;}
// server.c - un semplice server ZeroMQ con libzmq#include <stdio.h>#include <zmq.h>// main() - funzione mainint main(void){// creo il context e il responderprintf ("Avvio server...\n");void *context = zmq_ctx_new();void *responder = zmq_socket(context, ZMQ_REP);// associo l'indirizzo del responder al contestozmq_bind(responder, "tcp://*:5555");// loop di ricezionechar string[10];while (zmq_recv(responder, string, 10, 0) != -1) {printf("richiesta ricevuta: %s\n", string);// invio la rispostazmq_send(responder, "Pong", 5, 0);}// disconnettozmq_close(responder);zmq_ctx_destroy(context);return 0;}
E già questo codice dimostra che ZeroMQ è una libreria che permette di scrivere codice di rete molto semplificato. Ma con CZMQ possiamo snellirlo ancora di più (ma senza aspettarci miracoli: la libzmq già di per sé un passo avanti rispetto al vero low-level, che è rappresentato dai BSD Socket). Vai di nuovo col codice!
// client.c - un semplice client ZeroMQ con CZMQ#include <stdio.h>#include <czmq.h>// main() - funzione mainint main(void){// create requesterprintf ("Connessione al server...\n");zsock_t *requester = zsock_new_req("tcp://localhost:5555");// loop di invioint cnt = 0;while (cnt++ < 10) {// invio la richiestazstr_send(requester, "Ping");// ricevo la rispostachar *string;string = zstr_recv(requester);printf("risposta ricevuta: %s\n", string);zstr_free(&string);}// disconnettozsock_destroy(&requester);return 0;}
// server.c - un semplice server ZeroMQ con CZMQ#include <stdio.h>#include <czmq.h>// main() - funzione mainint main(void){// creo il responderprintf ("Avvio server...\n");zsock_t *responder = zsock_new_rep("tcp://*:5555");// loop di ricezionechar *string;while ((string = zstr_recv(responder)) != NULL) {printf("richiesta ricevuta: %s\n", string);zstr_free(&string);// invio la rispostazstr_send(responder, "Pong");}// disconnettozsock_destroy(&responder);return 0;}
Che ne dite? La differenza tra le tre versioni, pur non essendo eclatante, è evidente, e si nota una filosofia di programmazione abbastanza diversa. E, a parte le considerazioni filosofiche, alla fin fine si può dire che nel percorso da BSD Socket a libzmq a CZMQ, le linee di codice vanno in diminuzione, e questo è il fattore molto importante che stavamo ricercando.
Ok, per oggi può bastare: ZeroMQ passa, a pieni voti, l'esame di "ottima libreria per Software di rete". Lo passa bene già con la libreria low-level e "più meglio" (ah ah ah) con CZMQ, la libreria high-level. Ma, nonostante questo, vi invito a non dimenticare che, sotto sotto (anche sotto ZeroMQ!), ci sono i mitici e insostituibili BSD Socket che possono essere ancora usati direttamente per scrivere ottimo e soddisfacente codice di rete, magari solo un po' più complicato (da scrivere e da leggere). De gustibus...
Ciao, e al prossimo post!
Nessun commento:
Posta un commento