WebSockets and Node.js

Questo post esaminerà l’implementazione di librerie WebSocket di basso livello con Node.js. Vedremo come viene utilizzata ciascuna libreria e perché potresti sceglierla per il tuo progetto.

WebSockets and Node.js

I WebSocket consentono agli sviluppatori di creare funzionalità in tempo reale nelle loro app consentendo l’invio di piccoli blocchi di dati su un’unica connessione persistente, in entrambe le direzioni.

L’utilizzo di WebSocket nel front-end è abbastanza semplice, poiché è presente un’API WebSocket integrata in tutti i browser moderni. Per utilizzarli sul server, è necessaria un’applicazione di back-end. È qui che entra in gioco Node.js.

Node.js può mantenere molte centinaia di connessioni WebSocket contemporaneamente.

I WebSocket sul server possono diventare complicati poiché l’aggiornamento della connessione da HTTP a WebSocket richiede la gestione. Questo è il motivo per cui gli sviluppatori usano comunemente una libreria per gestirlo per loro. Esistono alcune librerie di server WebSocket comuni che semplificano la gestione di WebSocket, in particolare WS, SockJS e Socket.io.

WS: una libreria WebSocket di Node.js

WS è un server WebSocket per Node.js. È di livello piuttosto basso: ascolti le richieste di connessione in arrivo e rispondi ai messaggi non elaborati come stringhe o buffer di byte. Poiché i WebSocket sono supportati in modo nativo in tutti i browser moderni, è possibile lavorare con WS sul server e l’API WebSocket del browser sul client.

WebSockets Server

Innanzitutto, richiedi la libreria WS e utilizza il metodo WebSocket.Server per creare un nuovo server WebSocket sulla porta 7071 (nessun significato, qualsiasi porta va bene!).

const WebSocket = require('ws'); 
const wss = new WebSocket.Server({ port: 7071 });

Quindi, crea una mappa per archiviare i metadati di un client (tutti i dati che desideriamo associare a un client WebSocket):

const clients = new Map();

Iscriviti all’evento di connessione WSS utilizzando la funzione wss.on, fornendo una richiamata. Verrà chiamato ogni volta che un nuovo client WebSocket si connette al server:

wss.on('connection', (ws) => {
    const id = uuidv4();
    const color = Math.floor(Math.random() * 360);
    const metadata = { id, color };

    clients.set(ws, metadata);

Ogni volta che un client si connette, generiamo un nuovo ID univoco, che viene utilizzato per identificarlo. Ai client viene anche assegnato un colore del cursore utilizzando Math.random(); questo genera un numero compreso tra 0 e 360, che corrisponde al valore della tonalità di un colore HSV. L’ID e il colore del cursore vengono quindi aggiunti a un oggetto che chiameremo metadati e utilizziamo la mappa per associarli alla nostra istanza ws WebSocket. La mappa è un dizionario: possiamo recuperare questi metadati chiamando get e fornendo un’istanza WebSocket in un secondo momento. Utilizzando l’istanza WebSocket appena connessa, ci iscriviamo all’evento messaggio di tale istanza e forniamo una funzione di callback che verrà attivata ogni volta che questo client specifico invia un messaggio al server.

ws.on('message', (messageAsString) => {

Ogni volta che il nostro server riceve un messaggio, utilizziamo JSON.parse per ottenere il contenuto del messaggio e carichiamo i metadati del nostro client per questo socket dalla nostra mappa utilizzando clients.get(ws).

Aggiungeremo le nostre due proprietà di metadati al messaggio come mittente e colore…

const message = JSON.parse(messageAsString);
      const metadata = clients.get(ws);

      message.sender = metadata.id;
      message.color = metadata.color;

Quindi stringiamo nuovamente il nostro messaggio e lo inviamo a ogni client connesso.

const outbound = JSON.stringify(message);

      [...clients.keys()].forEach((client) => {
        client.send(outbound);
      });
    });

infine, quando un client chiude la sua connessione, rimuoviamo i suoi metadati dalla nostra mappa.

ws.on("close", () => {
      clients.delete(ws);
    });
});

In fondo abbiamo una funzione per generare un ID univoco.

function uuidv4() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}
console.log("wss up");

Questa implementazione del server esegue il multicast, inviando qualsiasi messaggio ricevuto a tutti i client connessi.