Creazione di un server di messaggistica utilizzando TCP/IP e Node.JS

Come realizzare da zero un piccolo server TCP/IP per la messaggistica istantanea (chat) utilizzando gli strumenti messi a disposizione da Node.JS

Node.js è una tecnica di scripting server side che utilizza un linguaggio molto simile a JavaScript. Nelle scorse lezioni abbiamo introdotto i principali moduli di Node.js e visto come realizzare un piccolo server HTTP. Con la stessa facilità è possibile realizzare un server che comunica ad un livello più basso rispetto al protocollo HTTP. In termini rigorosi ciò significa scendere nella pila dei livelli del modello OSI, perché la comunicazione HTTP avviene a livello di trasporto (4° livello del modello OSI), mentre la comunicazione via TCP/IP avviene a livello di rete (3° livello del modello OSI).

Se non conosciamo questa terminologia, o non ci interessa inquadrare l’argomento del modello OSI, non preoccupiamoci: per realizzare un piccolo server TCP/IP non servirà alcuna competenza sull’argomento. Le uniche nozioni necessarie sono quelle viste nelle precedenti lezioni, e riguardano solamente la conoscenza di JavaScript e Node.js. Una vaga idea del funzionamento della rete e dei suoi protocolli può tornare utile, ma non è strettamente necessaria.

Per rendere l’argomento comprensibile a chi non ha particolari competenze sistemistiche, introduciamo brevemente lo scenario di lavoro. Il concetto di server HTTP può essere considerato, a grandi linee, un sinonimo di Web server. In parole povere ciò significa che nella pratica la comunicazione coinvolge, dal lato del client, un normale browser. Un server TCP/IP non è molto diverso, ma lavorando ad un livello più basso richiede, da parte del client, l’utilizzo di un programma diverso dal browser. Le altre differenze riguardano questioni meramente sistemistiche, che non ci interessano in questa sede.

 

chat_01

 

 Figura 1 – Esempio di chat via TCP/IP

 

Prima di iniziare a discutere il codice del server, armiamoci di un client opportuno. Se lavoriamo con sistemi Unix-like questo non dovrebbe essere un problema, perché probabilmente il comando telnet è disponibile da linea di comando. Se invece usiamo Windows, dobbiamo attivarlo passando per il pannello di controllo. In particolare ciò significa cliccare, dal pannello di controllo, sull’etichetta “Attivazione e Disattivazione delle funzionalità di Windows”. A questo punto si aprirà una finestra dove possiamo spuntare la voce “Client Telnet”. Il sistema operativo richiederà qualche minuto per attivare questo aggiornamento, quindi non preoccupiamoci se l’operazione non è istantanea.

Chi lavora sul Web potrebbe chiedersi a cosa serve imparare a lavorare così a basso livello, tirando in ballo un “vecchio” protocollo come TCP/IP. Innanzitutto è bene ricordare che il protocollo TCP/IP non è vecchio nel senso di “obsoleto”, ma nel senso di longevo. La nascita di Internet può essere fatta coincidere con l’invenzione proprio di questo protocollo, e a tutt’oggi la grande maggioranza delle informazioni che viaggiano sul Web si basano su di esso. In secondo luogo un protocollo di questo tipo è molto più utile di quanto sembri, perché permette di gestire tutte le comunicazioni dirette tra macchine diverse. Ad esempio, quando usiamo un programma di chat non integrato con browser, con molta probabilità stiamo usando il protocollo TCP/IP.

Siamo finalmente pronti a passare alla pratica. Per dimostrare la semplicità di realizzazione di un server TCP/IP useremo Node.js proprio per creare una piccola chat basata su tale protocollo. Per i motivi discussi sopra, questa chat non potrà essere usata nel browser, ma richiederà l’utilizzo di un programma come telnet.

Il modulo Net

Il modulo Net

Prima di vedere il codice della chat, facciamo un po’ di pratica colmodulo Net. Questo è il modulo di Node.js che permette di gestire la comunicazione basata sul protocollo TCP/IP. Come vedremo tra poco il codice è molto simile a quello usato nelle prime lezioni per realizzare un server HTTP. Le differenze sono davvero minime. L’unica nozione che di interesse introdurre è il concetto di socket.

 

Un socket può essere pensato come un “tubo” che collega client e server. Quando apriamo la connessione (il socket) significa che i due interlocutori (client e server) prendono in mano il tubo. In qualunque momento ciascuno dei due può parlare attraverso il tubo, o trasmettere un messaggio, un po’ come si faceva con la posta pneumatica. Ciò non è molto diverso, almeno a grandi linee, dalla normale comunicazione via HTTP. Data questa somiglianza il codice del server può ricalcare senza alcun problema quello usato per implementare un server HTTP. Abbiamo così

 

 

La somiglianza col codice del server HTTP delle prime lezioni è evidente. Vediamo quali sono le differenze: ovviamente, nella prima riga, invece di caricare in modo HTTP carichiamo il modulo Net, per rendere disponibili gli oggetti e le API relative al protocollo TCP/IP. L’altra differenza sta nella funzione socket.on('data',...): questa viene invocata tutte le volte che l’applicazione client comunica qualcosa attraverso il socket (il nostro tubo). In questo caso, quando il server si accorge dell’arrivo di alcuni dati, viene invocata la callback (anonima) che contiene l’istruzione

 

 

la quale si occupa di scrivere il dato appena ricevuto nel socket. In altre parole il codice qui sopra è un sistema di eco, nel vero senso del termine: tutto ciò che il client scrive nel socket viene ripetuto dal server, e rispedito al mittente.

Per testare il funzionamento del server possiamo lanciare l’applicazione telnet

 

 

Come risultato otterremo prima il messaggio “Hello World!”, scritto dal server in risposta al nostro collegamento. Subito dopo, se proviamo a scrivere qualcosa, vedremo che il nostro testo risulta duplicato, proprio per effetto dell’eco.

Esempio di chat

Esempio di chat

Come visto a pagina precedente, la creazione di un piccolo server TCP/IP richiede pochissime righe di codice, che tra l’altro assomigliano molto a quelle di un normale server HTTP. Per trasformare il nostro “serverino” TCP/IP in un software capace di realizzare il lato server-side di una chat, dobbiamo apportare qualche modifica. Innanzitutto ci servirà una struttura dati dove memorizzare il numero di socket gestiti dal server. Supponendo ad esempio di avere 10 utenti collegati in chat, avremo un socket per ciascun utente. Ciò significa che il server ascolterà i messaggi ricevuti su tutti e 10 i socket, e poi si occuperà di inoltrarli agli altri client. La soluzione più semplice, anche se non molto professionale, è quella di usare un banale array:

 

 

se conosciamo le API degli array JavaScript, leggere il codice qui sopra dovrebbe essere abbastanza semplice. Le novità più interessanti rispetto all’esempio di pagina precedente riguardano infatti la gestione dell’array, dove memorizziamo tutti i socket aperti dai diversi utenti della chat. In particolare usiamo le funzioni push e splice, praticamente identiche a quelle JavaScript, per rispettivamente aggiungere e rimuovere elementi dall’array. Trattandosi di operazioni tipiche del paradigma JavaScript, non dovrebbe essere necessario approfondirle: se non conosciamo queste funzioni possiamo tranquillamente consultare qualunque documentazione JavaScript.

Oltre alla gestione dell’array, eseguita nell’ambito del paradigma JavaScript, le novità che riguardano strettamente Node.js sono due: l’inoltro dei messaggi ai diversi client, implementato dalla funzione socket.on('data',...), e la gestione della chiusura delle connessione da parte di un client, implementato dalla funzionesocket.on('end',...). Nel primo caso scorriamo semplicemente, con un ciclo for, l’elenco di tutti client attualmente connessi alla chat, escludendo quello corrente per evitare l’effetto dell’eco. Nel secondo caso ci occupiamo di rimuovere il canale di comunicazione chiuso dall’utente dal nostro array: non è necessario fare altro, perché tutta la gestione del protocollo TCP/IP viene svolta dietro le quinte da Node.js. Noi dobbiamo soltanto occuparci di gestire l’inserimento e cancellazione delle connessioni nella struttura dati che abbiamo scelto (in questo caso un array).

Se confrontiamo il codice del server TCP/IP con quello del server HTTP, vedremo che nella versione HTTP non era necessario gestire la chiusura del canale di comunicazione: è proprio questa la differenza principale, dal punto di vista funzionale, tra i due protocolli. Il protocollo TCP/IP si dice “stateful”, perché mantiene aperta la connessione, mentre quello HTTP si dice “stateless”, perché la connessione viene chiusa dopo ogni scambio di informazioni.

Il codice del server, se teniamo conto che stiamo realizzando un sistema di chat, è davvero semplice. Anche in questo caso, per testare il corretto funzionamento del sistema possiamo usare la funzione telnet. Per una verifica “più seria” conviene aprire almeno due finestre del terminale, e controllare che si parlino l’un l’altra. Il codice qui sopra rappresenta solo un esempio per introdurre le potenzialità di Node.js. Chiaramente, se dovessimo implementare sistema di chat professionale, probabilmente vorremmo gestire anche tutti quelli arricchimenti tipici delle chat. Inoltre, sempre nello scenario di un’ipotetica chat professionale, dobbiamo anche ricordarci di gestire tutti i possibili errori e/o situazioni non previste.

Facci sapere cosa ne pensi!

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *