Nel precedente articolo abbiamo introdotto node.js dicendo che il framework si presta molto bene per le applicazioni real-time. Adesso vediamo come sfruttarne le potenzialità per creare una semplice chat in real-time utilizzando le varie librerie esistenti per Node messe a nostra dispozione.
I requisiti di sistema
La prima cosa che ci serve è Node.js installato sul proprio pc. Per gli utenti Windows e Mac è possibile scaricare l’installer dal sito ufficiale www.nodejs.org oppure scaricare direttamente l’eseguibile node.exe senza bisogno di installare nulla sul proprio pc. Per gli utenti Linux è possibile utilizzare gli appositi repository e installare con apt-get nel caso di distribuzioni Debian o yum, nel caso di Fedora, CentOs o simili.
Dopo aver installato Node, avremo di bisogno queste dipendenze:
- ExpressJS – che gestisce il server e le risposte agli utenti
- Jade – un linguaggio per creare template html utilizzando una particolare notazione
- Socket.io – che permette lo scambio in realtime tra il server e client
Non è necessario installare queste librerie. Per recuperarle basta creare nel folder di lavoro un documento di testo che dovrà essere rinominato in package.json contenente questa dichiarazione:
{ "name": "RealTimeWebChat", "version": "0.0.0", "description": "Real time web chat", "dependencies": { "socket.io": "latest", "express": "latest", "jade": "latest" }, "author": "nome-developer" }
Bisogna poi aprire la console (o il prompt dei comandi), navigare fino alla cartella di lavoro ed eseguire il comando:
npm install
Npm è un package manager, che viene installato contestualmente a Node e si usa da riga di comando. Dopo aver eseguito il comando, Npm si occuperà di reperire le ultime versioni delle dipendenze dichiarate nel package.json che verranno “scaricate” all’interno della cartella node_modules
Costruiamo il server
Sviluppiamo, innanzitutto, un semplice server che mostrerà la pagina HTML per poi proseguire con la parte più interessante, cioè la gestione della comunicazione real time. Creiamo un file index.js e scriviamo il codice qui sotto, utilizzando come core ExpressJs:
var express = require("express"); var app = express(); var port = 3700; app.get("/", function(req, res){ res.send("Funziona!"); }); app.listen(port); console.log("Server in ascolto nella porta " + port);
Abbiamo creato un’applicazione ExpressJs e definita la porta che deve essere ascoltata. Successivamente abbiamo registrato una route (rotta o percorso) che, in questo caso, punta alla root ed è una semplice richiesta GET senza alcun parametro. Per il momento la route non fa altro che ritornare la stringa di testo “Funziona” al client che invia la richiesta.
Per avviare il server, digitare dalla console di sistema il comando:
node index.js
Se tutto è stato scritto correttamente e non ci sono errori, dovrebbe apparire il messaggio “Server in ascolto nella porta 3700” e collegandosi all’URL http://127.0.0.1:3700 dovrebbe apparire la scritta “Funziona!”. Ora, invece di questa scritta, vogliamo mostrare la pagina HTML, che creeremo utilizzando la sintassi particolare di Jade (per maggiori informazioni, clicca qui) che si integra perfettamente con ExpressJs. Creiamo quindi una sotto directory chiamata template e al suo interno creiamo un file chiamato page.jade con il seguente codice:
doctype html html(lang="it") head title= "Real time web chat" body #content(style='width: 500px; height: 300px; margin: 0 0 20px 0; border: solid 1px #999; overflow-y: scroll;') .controls input#field(style='width:350px;') input#send(type='button', value='send')
Nel file sopra abbiamo scritto la nostra pagina HTML in linguaggio Jade. La pagina contiene semplicemente un div (con id #content) per mostrare i messaggi inviati e in arrivo e due controlli (textbox e pulsante) per scrivere e inviare messaggi.
Adesso bisogna dire a ExpressJs dove si trova il template e come deve essere utilizzato. La nostra pagina index.js dovrà essere modificata come l’esempio in basso:
var express = require("express"); var app = express(); var port = 3700; app.set('views', __dirname + '/template'); app.set('view engine', "jade"); app.engine('jade', require('jade').__express); app.get("/", function(req, res){ res.render("page"); }); app.listen(port); console.log("Listening on port " + port);
Nella funzione di callback della route / abbiamo sostituito il metodo send con render, che renderizza il file Jade, lo trasforma in HTML e lo invia in output. Il server index.js deve essere stoppato e riavviato, altrimenti non si vedranno mai le nuove modifiche.
Per il lato client utilizzeremo un file javascript esterno al file html contenente lo script di funzionamento del client, che memorizzeremo in una cartella chiamata asset. E’ necessario “informare” ExpressJs su dove andare a reperire questo file. Per cui aggiungiamo questa riga di istruzioni prima di chiamare il metodo listen nel file index.js:
app.use(express.static(__dirname + '/public'));
Adesso è il momento di aggiungere la comunicazione real-time e per fare ciò, utilizzeremo la libreria Socket.io. La riga
app.listen(port);
va modificata come di seguito:
var io = require('socket.io').listen(app.listen(port));
Questa riga è stata compattata di proposito ma nessuno ci vieta di definire in una riga la variabile io, e chiamare in una seconda riga il metodo listen. A voi la scelta.
Andando avanti, dobbiamo scrivere il codice per gestire il messaggio ricevuto dal client e inviarlo a tutti gli altri client collegati, come una sorta di broadcasting. Quindi abbiamo bisogno di scrivere il codice che permette al server di ricevere un messaggio da un client connesso, e di inviarlo a tutti gli altri. Ogni applicazione Socket.io inizia con un handler connection. Tipo questo:
io.sockets.on('connection', function (socket) { socket.emit('message', { message: 'Benvenuto in chat' }); socket.on('send', function (data) { io.sockets.emit('message', data); }); });
La variabile socket che passiamo nella funzione di callback dell’handler connection, è il socket del client. Quindi la “connessione” tra il server e il client connesso. Se la connection viene eseguita (cioè il client si connette con successo) viene “emesso” il messaggio “Benvenuto in chat”, quindi il socket resta in ascolto di un messaggio (on send). Alla ricezione del messaggio, il socket invia il messaggio ricevuto in broadcast a tutti i client connessi attraverso io.sockets.emit. Semplice, no?
Costruiamo il client
Avevamo già anticipato di utilizzare un file javascript esterno per gestire la logica di funzionamento della chat lato client. Bene, creiamo un file chiamato chat.js e lo posizioniamo nella cartella asset. Incolliamo il seguente codice:
window.onload = function() { var messages = []; var socket = io.connect('http://localhost:3700'); var field = document.getElementById("field"); var sendButton = document.getElementById("send"); var content = document.getElementById("content"); socket.on('message', function (data) { if(data.message) { messages.push(data.message); var html = ''; for(var i=0; i<messages.length; i++) { html += messages[i] + '<br />'; } content.innerHTML = html; } else { console.log("Errore:", data); } }); sendButton.onclick = function() { var text = field.value; socket.emit('send', { message: text }); }; }
La parte, a mio parere, da attenzionare è quella relativa al socket. Qui, come avviene nel server index.js, si resta in ascolto di un messaggio (in questo caso ricevuto dal server) che viene accodato dentro il DIV appena ricevuto. Inoltre, viene definito un metodo sull’evento onclick del pulsante presente nella pagina, che utilizza il socket per inviare al server il messaggio digitato.
Ovviamente, abbiamo bisogno di modificare la pagina html per includere gli script necessari (chat.js e il socket). Andiamo a modificare il file page.jade aggiungendo le righe di codici subito sotto il tag title dell’header, così:
head title= "Real time web chat" script(src='/chat.js') script(src='/socket.io/socket.io.js')
Avete sicuramente notato che in realtà non esiste una directory /socket.io/ nella vostra cartella di lavoro, ne tantomeno un file chamato socket.io.js. Non vi preoccupate in quanto Socket.io gestisce automaticamente la richiesta e il download del file richiesto.
Non ci resta adesso che riavviare il nostro server, aggiornare la pagina HTML dal browser e iniziare a chattare (possiamo caricare la pagina su più browser o più schede e si noterà che i messaggi verranno correttamente inviati a tutti i client connessi).
Questo si tratta di un esempio molto banale su come utilizzare il Socket.io ed effettuare una connessione real-time. Si può pensare di estendere questa semplice applicazione in modo tale da abbellire ulteriormente la pagina con un foglio di stile, aggiungere un campo relativo al nome e stamparlo assieme al messaggio, aggiungere la data e l’ora di invio del messaggio, la data di connessione, la notifica di disconnessione di un client e tanto tanto altro.