Broadcast con Socket.io e Node.js
Inviare messaggi a tutti gli utenti connessi

Tempo di lettura: 6 minuti
Come creare messaggi broadcast con Socket.io e Node.js

Comunicare con molti clients simultaneamente

In tutti gli esempi precedenti, abbiamo ipotizzato di aver un server e un solo client. In pratica, probabilmente avrai tanti client collegati alla tua applicazione Node.js (almeno spero per te!).

Ovviamente, possiamo simulare questa situazione localmente, è molto semplice:  ci basta aprire due schede, entrambe sulla pagina http://localhost:8080.

Il server vedrà collegati 2 diversi client.

Quando adesso abbiamo più client connessi, dobbiamo essere in grado di:

  • Invia un messaggio a tutti contemporaneamente (broadcast).
  • Ricordati le informazioni di ogni client (come ad esempio il nome utente). Per questo abbiamo bisogno di variabili di sessione.

Questo è esattamente ciò che arriverai a fare alla fine di questo tutorial!

Invio di un messaggio a tutti i client (broadcast)

Quando si esegue una socket.emit() lato server, si invia un messaggio al client con il quale si sta attualmente parlando. Ma si può fare meglio di questo: si può inviare una trasmissione, cioè un messaggio destinato a tutti gli altri client (eccetto quello a cui il server si è appena connesso).

Consideriamo questo scenario:

  1. Client A invia un messaggio al server.
  2. Il server lo analizza.
  3. Decide di trasmettere questo messaggio e di inviarlo agli altri client connessi: B e C.
eventi-broadcast-trasmissioni-socketio-nodejs
Nel Broadcast il server invia un messaggio a tutti i client connessi

Per esempio immagina una chat. Client A scrive un messaggio e lo invia al server. Affinché gli altri client possano vedere il messaggio, deve essere trasmesso.

È semplice!

socket.broadcast.emit('message', 'Messaggio a tutte le unità. Ripeto, a tutte le unità.');

Basta fare un socket.broadcast.emit() e il messaggio andrà a tutti gli altri client collegati. Aggiungi una trasmissione in app.js, ad esempio quando un client si connette:

io.sockets.on('connection', function (socket) {
  socket.emit('message', 'Sei connesso amico!');
  socket.broadcast.emit('message', 'Si è appena connesso un altro visitatore!');

  socket.on('message', function (message) {
      console.log('Un visitatore vuole parlare con me! Dice: ' + message);
  });
});

Ora prova ad aprire 2 schede (o più) sulla pagina http://localhost:8080. Vedrai che quando arriva un nuovo client, le altre pagine reagiranno istantaneamente e diranno: “Si è appena connesso un altro visitatore”!

Ricapitoliamo, questo è il codice Javascript completo:

var http = require('http');
var fs = require('fs');

// Carichiamo il file index.html e mostriamo la pagina al visitatore
var server = http.createServer(function(req, res) {
  fs.readFile('./index.html', 'utf-8', function(error, content) {
    res.writeHead(200, {"Content-Type": "text/html"});
    res.end(content);
  });
});

// Carichiamo Socket.io
var io = require('socket.io').listen(server);

// Quando i client si connettono
io.sockets.on('connection', function (socket) {
  socket.emit('message', 'Sei connesso amico!');
  socket.broadcast.emit('message', 'Si è appena connesso un altro visitatore!');

  socket.on('message', function (message) {
      console.log('Un visitatore vuole parlare con me! Dice: ' + message);
  });
});

server.listen(8080);

Il nostro file html:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Socket.io</title>
</head>
<body>
  <h1>Aperta una comunicazione tramite Socket.io!</h1>

  <script src="/socket.io/socket.io.js"></script>
  <script>
    var socket = io.connect('http://localhost:8081');
    socket.on('message', function(message) {
      alert('Il server dice: ' + message);
    });
  </script>
</body>
</html>

Il risultato è questo:

broadcast-consocket-io-e-nodejs
I client già connessi ricevono una notifica per ogni nuovo client che entra

Ottimo, funziona!

Andiamo avanti e parliamo di sessioni.

Variabili di sessione

Quando abbiamo diversi client connessi, diventa difficile riconoscerli… L’ideale sarebbe essere in grado di memorizzare le informazioni su ogni client, ad esempio variabili di sessione!

Problema: le variabili di sessione non sono abilitate per default da Socket.io.

Le variabili di sessione vengono gestite in un’ altra libreria tramite un middleware già pronto, come ad esempio session.socket.io (esattamente come i middleware in Express, tipo un plugin).

Ci vorrebbe un intero tutorial dedicato alla gestione delle sessioni tramite middleware ma te lo risparmio.

Ti svelerò invece un trucco magico!

Basta salvare le informazioni come variabile nell’oggetto socket di ogni client.

Facilissimo!

Nota bene: ti suggerisco questa tecnica (molto semplificata) perché puoi provarla subito ma ti avviso che non è il metodo migliorare. Se vuoi avere più informazioni su come salvare le sessioni allora ti consiglio di dare un’occhiata alla documentazione di session.socket.io.

Noi vogliamo che il server si ricordi le informazioni di ogni client connesso. In questo modo, il client non avrà dirci chi è ogni volta che ci invia un nuovo messaggio!

Per memorizzare una variabile di sessione lato server, ci basta scrivere:

socket.myvariable = myvariable;

In questo esempio, memorizziamo i dati in una variabile nell’oggetto socket (ricorda che il server memorizza un nuovo oggetto socket per ogni client).

Per recuperare queste informazioni in futuro, sarà sufficiente vedere il contenuto di socket.myvariabile:

console.log(socket.myvariable);

Semplice, no?

Proviamo a immaginare un caso pratico. Riprendi in mano il codice del precedente tutorial, quello con il campanello come bottone, lo useremo come base per questo nuovo esercizio.

Ora, quando un client si connette, la pagina web chiede il proprio nome utente. Il server riporterà il nome utente nella variabile di sessione per utilizzarlo quando il client fa clic su pulsante.

Vediamo quali modifiche dobbiamo apportare…

1. La pagina web (index.html) emette un segnale contenente il nome utente.

Quando la pagina web viene caricata, chiederemo il nome utente del visitatore. Inviamo questo nome utente al server tramite un segnale “little_newbie” (l’ho chiamato così per differenziarlo dai segnali “message”). Questo segnale contiene il nome utente del visitatore:

var username = prompt('Come ti chiami amico?');
socket.emit('little_newbie', username);

2. Il server (app.js) memorizza il nome utente

Il server deve recuperare questo segnale. Poi ascolteremo il segnale “little_newbie” e, quando lo riceveremo, salveremo il nome utente come variabile di sessione:

socket.on('little_newbie', function(username) {
    socket.username = username;
});

Il server (app.js) ricorda il nome utente quando gli inviamo un messaggio.

Ora, vorremmo che il server si ricordasse il nome quando il client clicca il pulsante (che innesca l’ invio del segnale “messaggio”). Stiamo per completare la funzione di richiamo chiamata quando il server riceve un “messaggio”:

socket.on('message', function (message) {
    console.log(socket.username + ' sta parlando con me! Dice: ' + message);
});

Non appena riceviamo un messaggio, chiediamo che la variabile di sessione username venga recuperata dalla presa del client.

Prova del codice

Prova ad aprire ogni volta 2 finestre con nomi utente diversi. Quindi fai clic su l bottone.

Nella console vedrai il nome utente della persona che ha cliccato sul pulsante nella console!

Ho fatto il test con due finestre, una con lo username “alberto” e l’ altra con lo username “marco“. Guarda la console dell’immagine, puoi vedere che è riuscito perfettamente a distinguere gli chi ha effettuato il click!

variabili-di-sessione-socketio-nodejs
La variabile username viene salvata correttamente nella sessione.

Diversi client connessi: il server ricorda i loro nomi!

Il codice completo

Ti ho deliberatamente dato dei brevi frammenti di codice per spiegarti l’ idea, ma sono sicuro che stai morendo dalla voglia di avere il codice completo per effettuare i tuoi test.

Quindi andiamo! Ecco l’ index.html:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Socket.io</title>
</head>
<body>
  <h1>Aperta una comunicazione tramite Socket.io!</h1>

  <p>
    <input type="button" value="Suona il campanello" id="poke" />
  </p>

  <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
  <script src="/socket.io/socket.io.js"></script>
  <script>
    var socket = io.connect('http://localhost:8080');
    var username = prompt('Come ti chiami amico?');
    socket.emit('little_newbie', username);
    socket.on('message', function(message) {
      alert('Il server dice: ' + message);
    });

   $('#poke').click(function () {
     socket.emit('message', 'Hey server, ci sono anch\'io!');
   });
  </script>
</body>
</html>

e ecco l’ app del server applicativo:

var http = require('http');
var fs = require('fs');

// Carichiamo il file index.html e mostriamo la pagina al visitatore
var server = http.createServer(function(req, res) {
  fs.readFile('./index.html', 'utf-8', function(error, content) {
    res.writeHead(200, {"Content-Type": "text/html"});
    res.end(content);
  });
});

// Carichiamo Socket.io
var io = require('socket.io').listen(server);

// Quando i client si connettono
io.sockets.on('connection', function (socket) {
  socket.emit('message', 'Sei connesso amico!');

  socket.on('little_newbie', function(username) {
    socket.username = username;
  });

  socket.on('message', function (message) {
      console.log(socket.username + ' sta parlando con me! Dice: ' + message);
  });
});

server.listen(8080);

Spero di essere riuscito a spiegare abbastanza bene il codice in maniera che tu possa trovare la strada giusta.

Ricorda sempre che è un’app di base per provare le funzioni socket.io. Non fa nulla di interessante o affascinante, tocca a te divertirti giocando e sperimentando per fare pratica. Fai qualcosa di emozionante con questo… o almeno qualcosa di più utile di quello che ho fatto io 😉

Conclusione: Broadcast, sessioni e Socket.io con Node.js

  • Socket.io è un modulo Node.js che permette ai visitatori di comunicare in tempo reale con il server quando viene caricata la pagina.
  • Socket.io è basato su WebSockets, una sorta di ‘super AJAX‘. La tua applicazione può chiamare il server in qualsiasi momento senza ricaricare la pagina, e anche il contrario: i tuoi visitatori possono ricevere messaggi dal server in qualsiasi momento!
  • Il server e il client si inviano reciprocamente gli eventi (con socket.emit()) e ascoltano gli eventi inviati (con socket.on()).

L’articolo che hai appena letto fa parte di una lunga serie di guide e tutorial tradotte in italiano e distribuite gratuitamente. In particolare questo articolo si basa sui testi inglesi di Mathieu Nebra (Ultra fast applications using Node.js), con licenza CC BY-NC-SA. Questo articolo è sotto la stessa licenza.