Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 92 di 112
  • livello avanzato
Indice lezioni

Server-Sent Events (EventSource)

Realizzare comunicazioni real-time metterci in ascolto di eventi (push) generati dal server e gestirli quando il server ha qualcosa da comunicarci
Realizzare comunicazioni real-time metterci in ascolto di eventi (push) generati dal server e gestirli quando il server ha qualcosa da comunicarci
Link copiato negli appunti

Abbiamo visto come Ajax ci consenta di interagire con il server in maniera elegante ed efficiente inviando richieste HTTP ed elaborando le relative risposte senza dover ricaricare l'intera pagina.

Immaginiamo uno scenario in cui un'applicazione ha bisogno di elaborare dati in tempo reale, quando questi sono disponibili sul server. Pensiamo ad esempio ad un sistema di messaggistica istantanea o al monitoraggio di informazioni sullo stato di avanzamento di un processo sul server o ancora al monitoraggio di una grandezza misurata da una board IoT.

Per gestire situazioni di questo tipo con Ajax dovremmo prevedere un meccanismo di polling che a intervalli regolari invii una richiesta al server per verificare l'esistenza di nuove informazioni.

Le informazioni potrebbero non essere disponibili a intervalli regolari, ciò implicherebbe una serie di chiamate al server praticamente inutili, sprecando risorse di calcolo sia sul client che sul server.

In questi ed in altri contesti analoghi possiamo utilizzare i Server-Sent Events, tecnologia nota anche come EventSource dal nome dell'oggetto JavaScript che ne consente l'implementazione.

Grazie a questa tecnologia possiamo metterci in ascolto di eventi generati dal server e gestirli quando il server ha qualcosa da comunicarci.

Il seguente è un semplice esempio di utilizzo:

var eventSource = new EventSource("/myServerpage");
eventSource.addEventListener("message", function(e) {
	var myDiv = document.getElementById("myDiv");
	myDiv.innerHTML = e.data;
});

Come possiamo vedere dal codice, abbiamo creato un'istanza di EventSource facendo riferimento ad una pagina sul server. Abbiamo quindi definito un gestore dell'evento message, in corrispondenza del quale visualizziamo su un <div> i dati inviati dal server e recuperati tramite la proprietà e.data.

Una volta definito il gestore d'evento, visualizzeremo sulla pagina le informazioni inviate dal server quando queste saranno disponibili, senza alcuna attesa attiva da parte di JavaScript.

Diventa fondamentale a questo punto comprendere come il server deve inviare i dati al browser. Innanzitutto il Content-Type associato alla risposta da parte del server deve essere text/event-stream. Nel più semplice dei casi il contenuto è costituito da una riga che inizia con data: e termina con due fine riga consecutivi:

data: messaggio

La presenza di due ritorni a capo consecutivi (in pratica una riga vuota dopo la riga dei dati) indica la fine del messaggio. Ecco un esempio di codice server-side per inviare un messaggio al client, declinato in PHP:

header('Content-Type: text/event-stream');
echo "data: messaggio\n\n";
flush();

...e in C# in ambito ASP.NET

Response.ContentType = "text/event-stream";
Response.Write("data: messaggio\n\n");
Response.Flush();

Per inviare messaggi multiriga possiamo scrivere più righe consecutive, ma tutte devono iniziare con data:, come mostrato nell'esempio seguente:

data: questo è un messaggio
data: su più righe

Il contenuto di un messaggio viene trattato come una qualsiasi stringa, quindi nulla vieta di inviare dati nel formato che ci è più comodo. Ad esempio, possiamo inviare dati in formato JSON per poi analizzarlo opportunamente con JavaScript. Quindi se il server invia un messaggio come il seguente:

data: { "codice": "123",
data: "msg": "messaggio dal server" }

Il gestore d'evento deve effettuare il parsing del messaggio come mostrato di seguito:

eventSource.addEventListener("message", function(e) { 
	var myDiv = document.getElementById("myDiv");
	var messaggio = JSON.parse(e.data);
	myDiv.innerHTML = messaggio.msg;
});

È possibile associare un identificatore a ciascun messaggio includendo una riga che inizia con id:

id: 12345
data: { "codice": "123",
data: "msg": "messaggio dal server" }

La presenza di un identificatore consente al browser di tenere traccia dell'ultimo messaggio ricevuto e di risincronizzarsi con il server in caso di caduta della connessione, in modo da ricevere eventuali messaggi inviati nel frattempo dal server.

Lato server è anche possibile assegnare un nome d'evento a ciascun messaggio. Questo consente di differenziare i messaggi ricevuti sul browser e gestirli eventualmente in maniera appropriata.

Se per esempio il server genera i seguenti messaggi:

data: { "codice": "123",
data: "msg": "messaggio dal server" }
event: update
data: { "codice": "567",
data: "msg": "Nuovo messaggio dal server" }

Il primo messaggio sarà gestito dal gestore generico che intercetta l'evento standard message, come abbiamo già visto, mentre il secondo messaggio verrà gestito dal seguente gestore:

eventSource.addEventListener("update", function(e) { 
	var myDiv = document.getElementById("myDiv");
	var messaggio = JSON.parse(e.data);
	myDiv.innerHTML = "Nuovo aggiornamento: " + messaggio.msg;
});

In pratica, la stessa sorgente di eventi può generare eventi diversi che possono essere gestiti autonomamente l'uno dall'altro.

Due eventi predefiniti ci informano sullo stato della connessione. Il primo è l'evento open che si verifica al momento dell'apertura della connessione con il server:

eSource.addEventListener("open", function(e) { 
	console.log("Una connessione è stata aperta");
});

L'altro evento è error che ci informa di un problema di connessione o di comunicazione. Nella gestione dell'evento error possiamo avere informazioni sullo stato della connessione analizzando la proprietà readyState dell'oggetto event:

eSource.addEventListener("error", function(e) {
	if (e.readyState == EventSource.CLOSED) {
		console.log("La connessione è stata chiusa");
	}
});

Normalmente, se si verifica un errore di connessione il browser si riconnette automaticamente al server evitando di doverci occupare del monitoraggio della connessione. Quando intendiamo esplicitamente interrompere la connessione possiamo farlo sia lato client che lato server.

Sul client possiamo semplicemente invocare il metodo close():

eSource.close();

Sul server possiamo inviare del contenuto che non abbia text/event-stream come Content-Type oppure restituire un codice HTTP diverso da 200 OK.

Indipendentemente da chi interrompe la connessione, il browser non ritenterà la riconnessione automatica.

Ti consigliamo anche