MQTT – come usare MQTT in C – pt.3

  ICT, Rassegna Stampa
image_pdfimage_print
Logan: Lo sai che sono tutte balle, vero? Sarà successo un quarto di questa roba e non così. E nel mondo reale le persone muoiono e nessun presuntuoso in calzamaglia del cazzo può evitarlo!

E siamo giunti, finalmente, alla terza e ultima puntata di “MQTT – come usare MQTT in C”. E visto che la coerenza per me è molto importante, in questo articolo mostrerò come usare MQTT in Go (ah ah ah).

Beh, come ben sanno i miei lettori più assidui, io sono, fondamentalmente, un fanatico del C, però uso spesso (per lavoro) anche il Go e non disdegno di parlarne nei miei articoli; sarà, magari, perché è un bel linguaggio C-like, creato da due storici (e mitici) guru del mondo UNIX, Ken Thompson e Rob Pike, affiancati dal bravissimo Robert Griesemer (anche lui proveniente dal mondo UNIX). E non solo: il Go è la risposta di questi tre grandi informatici (e di Google) alle esagerate complicazioni introdotte nel C++11 (e non avevano ancora visto i successivi C++14/17/20/23!!!). In fondo all’articolo, per chi ha voglia di leggerlo, citerò uno scritto di Rob Pike sulla genesi del Go (che ho già citato qui e qui).

E la frase di Logan qui sopra? Beh, io direi che calza a pennello: nel mondo reale (della programmazione) bisogna fare scelte oculate (fregandosene delle mode) e il Go, per alcuni progetti, è una scelta oculata.

mqtt3
…meno balle: oggi usiamo il Go!…

Orbene, veniamo al dunque: ho scritto un programma mqttclient.go che è una replica delle due versioni in C pubblicate qui e qui. L’idea era che si potesse eseguire esattamente nello stesso modo e, ovviamente, produrre gli stessi risultati. Ok, vediamo un po se ci sono riuscito. Vai col codice!

// mqttclient.go - un semplice client mqtt in Go
package main import ( "fmt" "time" "os" mqtt "github.com/eclipse/paho.mqtt.golang"
) // funzione main
func main() { // test del numero di argomenti argc := len(os.Args) if (argc != 4) { // errore: numero errato di argomenti fmt.Printf("%s: numero errato di argomenti\n", os.Args[0]) fmt.Printf("uso: %s clientId topicPub topicSub (e.g.: %s client1 test/topic2 test/topic1)\n", os.Args[0], os.Args[0]) os.Exit(1) } // set delle opzioni di connessione opts := mqtt.NewClientOptions() opts.AddBroker(fmt.Sprintf("tcp://%s:%d", "localhost", 1883)) opts.SetClientID(os.Args[1]) opts.SetDefaultPublishHandler(onMessage) opts.OnConnect = onConnect opts.OnConnectionLost = onConnectionLost // mi collego client := mqtt.NewClient(opts) if token := client.Connect(); token.Wait() && token.Error() != nil { panic(token.Error()) } // eseguo subscribe e publish subscribe(client) publish(client) // mi disconnetto time.Sleep(2 * time.Second) client.Disconnect(250)
} // publish - funzione per pubblicare sui topic
func publish(client mqtt.Client) { // pubblico messaggi sull'altro topic (topicPub in "uso: %s clientId topicPub topicSub") for i := 0; i <= 2; i++ { // attendo 2 secondi prima di pubblicare un messaggio time.Sleep(2 * time.Second) // pubblico text := fmt.Sprintf("messaggio %d dal client %s", i, os.Args[1]) token := client.Publish(os.Args[2], 0, false, text) token.Wait() fmt.Printf("Messaggio %d publicato sul topic %s\n", i, os.Args[2]); }
} // subscribe - funzione per sottoscriversi ai topic
func subscribe(client mqtt.Client) { // mi sottoscrivo a un topic (topicSub in "uso: %s clientId topicPub topicSub") token := client.Subscribe(os.Args[3], 1, nil) token.Wait() fmt.Printf("%s: mi sottoscrivo al topic %s\n", os.Args[1], os.Args[3]);
} // onMessage - una callback per i messaggi che arrivano sul topic sorvegliato
var onMessage mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) { fmt.Printf("messaggio ricevuto sul topic %s: \"%s\"\n", msg.Topic(), msg.Payload())
} // onConnect - una callback per gestire la connessione
var onConnect mqtt.OnConnectHandler = func(client mqtt.Client) { //fmt.Println("Connected")
} // onConnectionLost - una callback per gestire la disconnessione
var onConnectionLost mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) { fmt.Printf("Connection lost: %v", err)
}

Come avrete notato, il codice è abbastanza semplice e super-commentato, quindi con c’è molto da spiegare, anche se per i lettori completamente a digiuno del Go non tutto sarà chiarissimo… ma questo non è un corso rapido di Go (ah ah ah). Vi do un consiglio spassionato: se vi interessa ampliare i vostri orizzonti e avere, anche, nuovi e interessanti sbocchi nel mondo del lavoro, studiatevi il Go! Si può cominciare con l’ottimo corso Go by Example, che è gratis ed è proposto e curato direttamente dal team ufficiale del Go (io ho cominciato, molti anni fa, proprio con questo corso e poi ho proseguito come autodidatta).

Chiusa la parentesi posso dire che, a parte eventuali incomprensioni da linguaggio, se fate un confronto con gli analoghi Client scritti in C dei precedenti articoli, noterete che il codice è veramente molto simile ed è moderatamente più compatto, visto che il Go è, praticamente, un C ad alto livello.

Faccio nuovamente notare che anche questo esempio si collega a un broker  locale (ossia installato sulla macchina stessa di esecuzione), quindi il collegamento è fatto con l’indirizzo: “tcp://localhost:1883”. Ma ci sono anche dei broker pubblici disponibili in rete che funzionano esattamente come quello locale. Nel mio caso (Linux) il broker  locale che uso è Mosquitto (disponibile anche per Windows) e funziona alla grande.

Ed è giunta l’ora di compilare ed eseguire. Qui, devo aggiungere qualche dettaglio: nelle versioni relativamente più recenti del Go (dalla versione 1.11) la compilazione dei programmi passa attraverso i Go Modules che sono il sistema standard ufficiale per la gestione delle dipendenze nei progetti Go. Questo semplifica un po’ la gestione rispetto al precedente sistema (che non è il caso di descrivere qui). In parole povere: prima di compilare bisogna preparare un file che si chiama go.mod, che normalmente sta nella stessa directory dove ci sono i sorgenti. Per crearlo ho scritto un piccolo script-shell (buildmod.sh) per facilitarmi (e facilitarvi) la vita: eccolo!

#!/bin/bash
rm go.mod
rm go.sum
rm mqttclient
go mod init mqttclient
go mod tidy

lanciando lo script vedrete qualcosa tipo questo:

aldo@Linux $ ./buildmod.sh
go: creating new go.mod: module mqttclient
go: to add module requirements and sums: go mod tidy
go: finding module for package github.com/eclipse/paho.mqtt.golang
go: found github.com/eclipse/paho.mqtt.golang in github.com/eclipse/paho.mqtt.golang v1.5.1

una volta creato il go.mod (solo per la prima compilazione, non serve ricrearlo ogni volta) si può compilare ed eseguire. Compiliamo (e questo è veramente semplicissimo):

aldo@Linux $ go build

ed eseguiamo in due terminali diversi (possibilmente in quasi-contemporanea, per non perdersi messaggi: se siete troppo lenti, quando il secondo client parte il primo potrebbe già aver pubblicato tutti i suoi messaggi, ah ah ah. Comunque le “strane” sleep che ho aggiunto sono proprio per rendere leggibili i risultati). Il risultato è questo:

TERMINALE 1:
aldo@Linux $ ./mqttclient Client2 test/topic1 test/topic2
Client2: mi sottoscrivo al topic test/topic2
messaggio ricevuto sul topic test/topic2: "messaggio 0 dal client Client1"
Messaggio 0 publicato sul topic test/topic1
messaggio ricevuto sul topic test/topic2: "messaggio 1 dal client Client1"
Messaggio 1 publicato sul topic test/topic1
messaggio ricevuto sul topic test/topic2: "messaggio 2 dal client Client1"
Messaggio 2 publicato sul topic test/topic1
TERMINALE 2:
aldo@Linux $ ./mqttclient Client1 test/topic2 test/topic1
Client1: mi sottoscrivo al topic test/topic1
Messaggio 0 publicato sul topic test/topic2
messaggio ricevuto sul topic test/topic1: "messaggio 0 dal client Client2"
Messaggio 1 publicato sul topic test/topic2
messaggio ricevuto sul topic test/topic1: "messaggio 1 dal client Client2"
Messaggio 2 publicato sul topic test/topic2
messaggio ricevuto sul topic test/topic1: "messaggio 2 dal client Client2"

Visto? Anche questa versione in Go funziona perfettamente (e identicamente) alle altre scritte in C!

Ok, per oggi può bastare. Nel prossimo articolo non so ancora di cosa parlerò, ma vi prometto che sarà, come sempre, molto interessante! Ah, come promesso all’inizio dell’articolo, dopo il solito saluto vi proporrò una citazione di Rob Pike sulla genesi del Go. Leggetevi la citazione (e magari anche l’intero articolo di Pike) è molto interessante!

Ciao, e al prossimo post!

"...Intorno a settembre 2007, stavo lavorando a una modifica minore ma fondamentale su un enorme programma C++ di Google, uno con cui tutti voi avete avuto a che fare, e le mie compilazioni impiegavano circa 45 minuti sul nostro gigantesco cluster di compilazione distribuito. Fu annunciato che ci sarebbe stata una presentazione tenuta da un paio di dipendenti di Google membri del comitato per gli standard C++. Avrebbero illustrato le novità in arrivo con il C++0x, come veniva chiamato all’epoca. (Oggi è noto come C++11). Nel corso di quell’ora di intervento abbiamo sentito parlare di circa 35 nuove funzionalità che erano in fase di progettazione. In realtà ce n’erano molte di più, ma solo 35 sono state descritte durante l’intervento. Alcune delle funzionalità erano minori, ovviamente, ma quelle citate nella presentazione erano almeno abbastanza significative da meritare di essere menzionate. Alcune erano molto sottili e difficili da comprendere, come i riferimenti rvalue, mentre altre sono particolarmente tipiche del C++, come i modelli variadici, e altre ancora sono semplicemente folli, come i letterali definiti dall’utente. A questo punto mi sono posto una domanda: il comitato del C++ credeva davvero che il problema del C++ fosse che non avesse abbastanza funzionalità? Sicuramente, in una variante della battuta di Ron Hardin, sarebbe un risultato maggiore semplificare il linguaggio piuttosto che arricchirlo. Ovviamente è ridicolo, ma tenete a mente l’idea...” da “Less is exponentially more”, Rob Pike, 2012

https://italiancoders.it/mqtt-come-usare-mqtt-in-c-pt-3/