Comprensione e gestione degli eventi di durata della connessione in SignalR
Avviso
Questa documentazione non è per la versione più recente di SignalR. Esaminare ASP.NET Core SignalR.
Questo articolo offre una panoramica degli eventi di connessione, riconnessione e disconnessione di SignalR che è possibile gestire e di timeout e impostazioni keepalive che è possibile configurare.
L'articolo presuppone che l'utente abbia già alcune conoscenze sugli eventi di durata della connessione e SignalR. Per un'introduzione a SignalR, vedere Introduzione a SignalR. Per gli elenchi degli eventi di durata della connessione, vedere le risorse seguenti:
- Come gestire gli eventi di durata della connessione nella classe Hub
- Come gestire gli eventi di durata della connessione nei client JavaScript
- Come gestire gli eventi di durata delle connessioni nei client .NET
Versioni software usate in questo argomento
- Visual Studio 2017
- .NET 4.5
- SignalR versione 2
Versioni precedenti di questo argomento
Per informazioni sulle versioni precedenti di SignalR, vedere Versioni precedenti di SignalR.
Domande e commenti
Lasciare commenti e suggerimenti su come è piaciuta questa esercitazione e su cosa è possibile migliorare nei commenti nella parte inferiore della pagina. Se si hanno domande non direttamente correlate all'esercitazione, è possibile pubblicarle nel forum ASP.NET SignalR o StackOverflow.com.
Panoramica
In questo articolo sono contenute le sezioni seguenti:
I collegamenti agli argomenti di riferimento sulle API sono relativi alla versione .NET 4.5 dell'API. Se si usa .NET 4, vedere gli argomenti sulla versione .NET 4 dell'API.
Terminologia e scenari relativi alla durata delle connessioni
Il OnReconnected
gestore eventi in un hub SignalR può essere eseguito direttamente dopo OnConnected
ma non dopo OnDisconnected
per un determinato client. Il motivo per cui è possibile avere una riconnessione senza disconnessione è che esistono diversi modi in cui la parola "connessione" viene usata in SignalR.
Connessioni SignalR, connessioni di trasporto e connessioni fisiche
Questo articolo distingue tra connessioni SignalR, connessioni di trasporto e connessioni fisiche:
- La connessione SignalR fa riferimento a una relazione logica tra un client e un URL del server, gestita dall'API SignalR e identificata in modo univoco da un ID connessione. I dati relativi a questa relazione vengono mantenuti da SignalR e vengono usati per stabilire una connessione di trasporto. La relazione termina e SignalR elimina i dati quando il client chiama il
Stop
metodo o viene raggiunto un limite di timeout mentre SignalR tenta di ristabilire una connessione di trasporto persa. - La connessione di trasporto fa riferimento a una relazione logica tra un client e un server, gestita da una delle quattro API di trasporto: WebSocket, eventi inviati dal server, fotogrammi per sempre o polling lungo. SignalR usa l'API di trasporto per creare una connessione di trasporto e l'API di trasporto dipende dall'esistenza di una connessione di rete fisica per creare la connessione di trasporto. La connessione di trasporto termina quando SignalR la termina o quando l'API di trasporto rileva che la connessione fisica è interrotta.
- La connessione fisica si riferisce ai collegamenti di rete fisica, cavi, segnali wireless, router e così via, che facilitano la comunicazione tra un computer client e un computer server. La connessione fisica deve essere presente per stabilire una connessione di trasporto e deve essere stabilita una connessione di trasporto per stabilire una connessione SignalR. Tuttavia, l'interruzione della connessione fisica non sempre termina immediatamente la connessione di trasporto o la connessione SignalR, come illustrato più avanti in questo argomento.
Nel diagramma seguente la connessione SignalR è rappresentata dall'API Hubs e dal livello Api PersistentConnection SignalR, la connessione di trasporto è rappresentata dal livello Transports e la connessione fisica è rappresentata dalle linee tra il server e i client.
Quando si chiama il Start
metodo in un client SignalR, si fornisce al client SignalR tutte le informazioni necessarie per stabilire una connessione fisica a un server. Il codice client SignalR usa queste informazioni per effettuare una richiesta HTTP e stabilire una connessione fisica che usa uno dei quattro metodi di trasporto. Se la connessione di trasporto non riesce o il server non riesce, la connessione SignalR non viene immediatamente interrotta perché il client ha ancora le informazioni necessarie per ristabilire automaticamente una nuova connessione di trasporto allo stesso URL signalR. In questo scenario non viene coinvolto alcun intervento dell'applicazione utente e quando il codice client SignalR stabilisce una nuova connessione di trasporto, non avvia una nuova connessione SignalR. La continuità della connessione SignalR si riflette nel fatto che l'ID connessione, che viene creato quando si chiama il Start
metodo, non cambia.
Il OnReconnected
gestore eventi nell'hub viene eseguito quando una connessione di trasporto viene ristabilita automaticamente dopo la perdita. Il OnDisconnected
gestore eventi viene eseguito alla fine di una connessione SignalR. Una connessione SignalR può terminare in uno dei modi seguenti:
- Se il client chiama il
Stop
metodo , viene inviato un messaggio di arresto al server e sia il client che il server terminano immediatamente la connessione SignalR. - Dopo la perdita della connettività tra client e server, il client tenta di riconnettersi e il server attende la riconnessione del client. Se i tentativi di riconnessione non sono riusciti e il periodo di timeout di disconnessione termina, sia il client che il server terminano la connessione SignalR. Il client smette di tentare di riconnettersi e il server elimina la relativa rappresentazione della connessione SignalR.
- Se il client smette di eseguire l'esecuzione senza avere la possibilità di chiamare il
Stop
metodo, il server attende la riconnessione del client e quindi termina la connessione SignalR dopo il periodo di timeout di disconnessione. - Se il server smette di eseguire l'esecuzione, il client tenta di riconnettersi (ricreare la connessione di trasporto) e quindi termina la connessione SignalR dopo il periodo di timeout della disconnessione.
Quando non sono presenti problemi di connessione e l'applicazione utente termina la connessione SignalR chiamando il Stop
metodo , la connessione SignalR e la connessione di trasporto iniziano e terminano contemporaneamente. Le sezioni seguenti descrivono in modo più dettagliato gli altri scenari.
Scenari di disconnessione del trasporto
Le connessioni fisiche potrebbero essere lente o potrebbero verificarsi interruzioni della connettività. A seconda di fattori come la lunghezza dell'interruzione, la connessione di trasporto potrebbe essere eliminata. SignalR tenta quindi di ristabilire la connessione di trasporto. A volte l'API di connessione del trasporto rileva l'interruzione e elimina la connessione di trasporto e SignalR rileva immediatamente che la connessione viene persa. In altri scenari, né l'API di connessione di trasporto né SignalR diventa immediatamente consapevole che la connettività è stata persa. Per tutti i trasporti tranne il polling lungo, il client SignalR usa una funzione denominata keepalive per verificare la perdita di connettività che l'API di trasporto non è in grado di rilevare. Per informazioni sulle connessioni di polling lunghe, vedere Timeout e keepalive settings più avanti in questo argomento.
Quando una connessione è inattiva, il server invia periodicamente un pacchetto keepalive al client. A partire dalla data in cui viene scritto questo articolo, la frequenza predefinita è ogni 10 secondi. Ascoltando questi pacchetti, i client possono stabilire se si è verificato un problema di connessione. Se un pacchetto keepalive non viene ricevuto quando previsto, dopo un breve periodo di tempo il client presuppone che si verifichino problemi di connessione, ad esempio lentezza o interruzioni. Se il keepalive non viene ancora ricevuto dopo un periodo di tempo più lungo, il client presuppone che la connessione sia stata eliminata e inizi a tentare di riconnettersi.
Il diagramma seguente illustra gli eventi client e server generati in uno scenario tipico quando si verificano problemi con la connessione fisica non immediatamente riconosciuta dall'API di trasporto. Il diagramma si applica alle circostanze seguenti:
- Il trasporto è WebSocket, per sempre frame o eventi inviati dal server.
- Esistono diversi periodi di interruzione nella connessione di rete fisica.
- L'API di trasporto non rileva le interruzioni, quindi SignalR si basa sulla funzionalità keepalive per rilevarle.
Se il client passa alla modalità di riconnessione ma non riesce a stabilire una connessione di trasporto entro il limite di timeout di disconnessione, il server termina la connessione SignalR. In questo caso, il server esegue il metodo dell'hub OnDisconnected
e accoda un messaggio di disconnessione da inviare al client nel caso in cui il client riesca a connettersi in un secondo momento. Se il client esegue la riconnessione, riceve il comando disconnect e chiama il Stop
metodo . In questo scenario, OnReconnected
non viene eseguito quando il client si riconnette e OnDisconnected
non viene eseguito quando il client chiama Stop
. Il diagramma seguente illustra questo scenario.
Gli eventi di durata della connessione SignalR che possono essere generati nel client sono i seguenti:
ConnectionSlow
evento client.Generato quando è stata ricevuta una proporzione predefinita del periodo di timeout keepalive dall'ultimo messaggio o ping keepalive. Il periodo di avviso di timeout keepalive predefinito è 2/3 del timeout keepalive. Il timeout keepalive è di 20 secondi, quindi l'avviso si verifica a circa 13 secondi.
Per impostazione predefinita, il server invia ping keepalive ogni 10 secondi e il client verifica la presenza di ping keepalive circa ogni 2 secondi (un terzo della differenza tra il valore di timeout keepalive e il valore di avviso di timeout keepalive).
Se l'API di trasporto diventa consapevole di una disconnessione, SignalR potrebbe essere informato della disconnessione prima che il periodo di avviso di timeout keepalive venga superato. In tal caso, l'evento
ConnectionSlow
non viene generato e SignalR passa direttamente all'eventoReconnecting
.Reconnecting
evento client.Generato quando (a) l'API di trasporto rileva che la connessione viene persa o (b) il periodo di timeout keepalive è passato dopo la ricezione dell'ultimo messaggio o ping keepalive. Il codice client SignalR inizia a tentare di riconnettersi. È possibile gestire questo evento se si vuole che l'applicazione esenesse un'azione quando si perde una connessione di trasporto. Il periodo di timeout keepalive predefinito è attualmente di 20 secondi.
Se il codice client tenta di chiamare un metodo hub mentre SignalR è in modalità di riconnessione, SignalR tenterà di inviare il comando. Nella maggior parte dei casi, tali tentativi avranno esito negativo, ma in alcune circostanze potrebbero avere esito positivo. Per gli eventi inviati dal server, per sempre i trasporti di polling e long polling, SignalR usa due canali di comunicazione, uno usato dal client per inviare messaggi e uno usato per ricevere messaggi. Il canale usato per la ricezione è quello aperto in modo permanente, ovvero quello chiuso quando la connessione fisica viene interrotta. Il canale usato per l'invio rimane disponibile, quindi se viene ripristinata la connettività fisica, una chiamata al metodo dal client al server potrebbe avere esito positivo prima che il canale di ricezione venga ristabilito. Il valore restituito non viene ricevuto finché SignalR non apre nuovamente il canale usato per la ricezione.
Reconnected
evento client.Generato quando la connessione di trasporto viene ristabilita. Viene
OnReconnected
eseguito il gestore eventi nell'hub.Closed
evento client (disconnected
evento in JavaScript).Generato quando scade il periodo di timeout di disconnessione mentre il codice client SignalR sta tentando di riconnettersi dopo aver perso la connessione di trasporto. Il timeout di disconnessione predefinito è 30 secondi. Questo evento viene generato anche al termine della connessione perché viene chiamato il
Stop
metodo .
Interruzioni della connessione di trasporto non rilevate dall'API di trasporto e non ritardare la ricezione di ping keepalive dal server per più tempo rispetto al periodo di avviso di timeout keepalive potrebbe non causare la generazione di eventi di durata della connessione.
Alcuni ambienti di rete chiudono deliberatamente le connessioni inattive e un'altra funzione dei pacchetti keepalive consiste nell'evitare questo problema consentendo a queste reti di sapere che è in uso una connessione SignalR. In casi estremi la frequenza predefinita dei ping keepalive potrebbe non essere sufficiente per impedire le connessioni chiuse. In tal caso è possibile configurare ping keepalive da inviare più spesso. Per altre informazioni, vedere Timeout e keepalive settings più avanti in questo argomento.
Nota
Importante: la sequenza di eventi descritti qui non è garantita. SignalR tenta di generare eventi di durata della connessione in modo prevedibile in base a questo schema, ma esistono molte varianti di eventi di rete e molti modi in cui i framework di comunicazione sottostanti, ad esempio le API di trasporto, li gestiscono. Ad esempio, l'evento Reconnected
potrebbe non essere generato quando il client si riconnette o il OnConnected
gestore nel server potrebbe essere eseguito quando il tentativo di stabilire una connessione non riesce. In questo argomento vengono descritti solo gli effetti che normalmente verrebbero prodotti da determinate circostanze tipiche.
Scenari di disconnessione client
In un client del browser, il codice client SignalR che gestisce una connessione SignalR viene eseguito nel contesto JavaScript di una pagina Web. Ecco perché la connessione SignalR deve terminare quando si passa da una pagina a un'altra ed è per questo che si hanno più connessioni con più ID di connessione se ci si connette da più finestre o schede del browser. Quando l'utente chiude una finestra o una scheda del browser o passa a una nuova pagina o aggiorna la pagina, la connessione SignalR termina immediatamente perché il codice client SignalR gestisce automaticamente l'evento del browser e chiama il Stop
metodo . In questi scenari o in qualsiasi piattaforma client quando l'applicazione chiama il Stop
metodo , il OnDisconnected
gestore eventi viene eseguito immediatamente sul server e il client genera l'evento Closed
(l'evento è denominato disconnected
in JavaScript).
Se un'applicazione client o il computer in cui è in esecuzione si arresta in modo anomalo o passa alla sospensione (ad esempio, quando l'utente chiude il portatile), il server non viene informato di ciò che è successo. Per quanto riguarda il server, la perdita del client potrebbe essere dovuta a un'interruzione della connettività e il client potrebbe tentare di riconnettersi. Pertanto, in questi scenari il server attende di consentire al client di riconnettersi e OnDisconnected
non viene eseguito fino alla scadenza del periodo di timeout di disconnessione (circa 30 secondi per impostazione predefinita). Il diagramma seguente illustra questo scenario.
Scenari di disconnessione del server
Quando un server passa offline, viene riavviato, ha esito negativo, il dominio dell'app viene riciclato e così via. Il risultato potrebbe essere simile a una connessione persa oppure l'API di trasporto e SignalR potrebbero sapere immediatamente che il server è andato e SignalR potrebbe iniziare a tentare di riconnettersi senza generare l'evento ConnectionSlow
. Se il client passa alla modalità di riconnessione e se il server viene ripristinato o riavviato o un nuovo server viene portato online prima della scadenza del periodo di timeout di disconnessione, il client si riconnetterà al server ripristinato o nuovo. In tal caso, la connessione SignalR continua sul client e l'evento Reconnected
viene generato. Nel primo server non OnDisconnected
viene mai eseguito e nel nuovo server viene OnReconnected
eseguito anche se OnConnected
non è mai stato eseguito per tale client in quel server in precedenza. L'effetto è lo stesso se il client si riconnette allo stesso server dopo un riavvio o un riciclo del dominio dell'app, perché quando il server la riavvia non ha memoria dell'attività di connessione precedente. Il diagramma seguente presuppone che l'API di trasporto acquisisca immediatamente la connessione persa, quindi l'evento ConnectionSlow
non viene generato.
Impostazioni di timeout e keepalive
I valori predefiniti ConnectionTimeout
, DisconnectTimeout
e KeepAlive
sono appropriati per la maggior parte degli scenari, ma possono essere modificati se l'ambiente ha esigenze particolari. Ad esempio, se l'ambiente di rete chiude le connessioni inattive per 5 secondi, potrebbe essere necessario ridurre il valore keepalive.
ConnectionTimeout
Questa impostazione rappresenta il tempo necessario per lasciare aperta una connessione di trasporto e attendere una risposta prima di chiuderla e aprire una nuova connessione. Il valore predefinito è 110 secondi.
Questa impostazione si applica solo quando la funzionalità keepalive è disabilitata, che in genere si applica solo al trasporto di polling lungo. Il diagramma seguente illustra l'effetto di questa impostazione su una connessione di trasporto di polling prolungata.
DisconnectTimeout
Questa impostazione rappresenta la quantità di tempo di attesa dopo la perdita di una connessione di trasporto prima di generare l'evento Disconnected
. Il valore predefinito è 30 secondi. Quando si imposta DisconnectTimeout
, KeepAlive
viene impostato automaticamente su 1/3 del DisconnectTimeout
valore.
KeepAlive
Questa impostazione rappresenta la quantità di tempo di attesa prima di inviare un pacchetto keepalive su una connessione inattiva. Il valore predefinito è 10 secondi. Questo valore non deve essere maggiore di 1/3 del DisconnectTimeout
valore.
Se si desidera impostare sia DisconnectTimeout
che KeepAlive
, impostare KeepAlive
dopo DisconnectTimeout
. In caso contrario, l'impostazione KeepAlive
verrà sovrascritta quando DisconnectTimeout
si imposta KeepAlive
automaticamente su 1/3 del valore di timeout.
Se si vuole disabilitare la funzionalità keepalive, impostare su KeepAlive
Null. La funzionalità Keepalive viene disabilitata automaticamente per il trasporto di polling lungo.
Come modificare le impostazioni di timeout e keepalive
Per modificare i valori predefiniti per queste impostazioni, impostarli nel Application_Start
file Global.asax , come illustrato nell'esempio seguente. I valori mostrati nel codice di esempio sono gli stessi dei valori predefiniti.
protected void Application_Start(object sender, EventArgs e)
{
// Make long polling connections wait a maximum of 110 seconds for a
// response. When that time expires, trigger a timeout command and
// make the client reconnect.
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(110);
// Wait a maximum of 30 seconds after a transport connection is lost
// before raising the Disconnected event to terminate the SignalR connection.
GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);
// For transports other than long polling, send a keepalive packet every
// 10 seconds.
// This value must be no more than 1/3 of the DisconnectTimeout value.
GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10);
RouteTable.Routes.MapHubs();
}
Come notificare all'utente le disconnessioni
In alcune applicazioni potrebbe essere necessario visualizzare un messaggio all'utente quando si verificano problemi di connettività. Sono disponibili diverse opzioni per come e quando eseguire questa operazione. Gli esempi di codice seguenti riguardano un client JavaScript che usa il proxy generato.
Gestire l'evento
connectionSlow
per visualizzare un messaggio non appena SignalR è a conoscenza dei problemi di connessione, prima di passare alla modalità di riconnessione.$.connection.hub.connectionSlow(function() { notifyUserOfConnectionProblem(); // Your function to notify user. });
Gestire l'evento
reconnecting
per visualizzare un messaggio quando SignalR è a conoscenza di una disconnessione e passa alla modalità di riconnessione.$.connection.hub.reconnecting(function() { notifyUserOfTryingToReconnect(); // Your function to notify user. });
Gestire l'evento
disconnected
per visualizzare un messaggio quando si è verificato il timeout di un tentativo di riconnessione. In questo scenario, l'unico modo per ristabilire una connessione con il server consiste nel riavviare la connessione SignalR chiamando ilStart
metodo , che creerà un nuovo ID connessione. L'esempio di codice seguente usa un flag per assicurarsi di eseguire la notifica solo dopo un timeout di riconnessione, non dopo una normale fine alla connessione SignalR causata dalla chiamata alStop
metodo .var tryingToReconnect = false; $.connection.hub.reconnecting(function() { tryingToReconnect = true; }); $.connection.hub.reconnected(function() { tryingToReconnect = false; }); $.connection.hub.disconnected(function() { if(tryingToReconnect) { notifyUserOfDisconnect(); // Your function to notify user. } });
Come riconnettersi continuamente
In alcune applicazioni potrebbe essere necessario ristabilire automaticamente una connessione dopo che è stata persa e il tentativo di riconnessione è scaduto. A tale scopo, è possibile chiamare il Start
metodo dal Closed
gestore eventi (disconnected
gestore eventi nei client JavaScript). È possibile attendere un periodo di tempo prima di chiamare Start
per evitare di eseguire questa operazione troppo spesso quando il server o la connessione fisica non sono disponibili. L'esempio di codice seguente riguarda un client JavaScript che usa il proxy generato.
$.connection.hub.disconnected(function() {
setTimeout(function() {
$.connection.hub.start();
}, 5000); // Restart connection after 5 seconds.
});
Un potenziale problema da tenere presente nei client mobili è che i tentativi di riconnessione continua quando il server o la connessione fisica non è disponibile potrebbe causare uno scaricamento della batteria non necessario.
Come disconnettere un client nel codice del server
SignalR versione 2 non dispone di un'API server predefinita per la disconnessione dei client. In futuro sono previsti piani per l'aggiunta di questa funzionalità. Nella versione corrente di SignalR, il modo più semplice per disconnettere un client dal server consiste nell'implementare un metodo disconnect nel client e chiamare tale metodo dal server. L'esempio di codice seguente illustra un metodo di disconnessione per un client JavaScript usando il proxy generato.
var myHubProxy = $.connection.myHub
myHubProxy.client.stopClient = function() {
$.connection.hub.stop();
};
Avviso
Sicurezza: né questo metodo per disconnettere i client né l'API predefinita proposta risolverà lo scenario dei client compromessi che eseguono codice dannoso, poiché i client potrebbero riconnettersi o il codice violato potrebbe rimuovere il stopClient
metodo o modificare le operazioni eseguite. La posizione appropriata per implementare la protezione DOS (Denial of Service) con stato non è nel framework o nel livello server, ma piuttosto nell'infrastruttura front-end.
Rilevamento del motivo di una disconnessione
SignalR 2.1 aggiunge un overload all'evento server OnDisconnect
che indica se il client è deliberatamente disconnesso invece di scadere. Il StopCalled
parametro è true se il client ha chiuso in modo esplicito la connessione. In JavaScript, se un errore del server ha portato il client a disconnettersi, le informazioni sull'errore verranno passate al client come $.connection.hub.lastError
.
Codice del server C#: stopCalled
parametro
public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
{
if (stopCalled)
{
Console.WriteLine(String.Format("Client {0} explicitly closed the connection.", Context.ConnectionId));
}
else
{
Console.WriteLine(String.Format("Client {0} timed out .", Context.ConnectionId));
}
return base.OnDisconnected(stopCalled);
}
Codice client JavaScript: accesso lastError
all'evento disconnect
.
$.connection.hub.disconnected(function () {
if ($.connection.hub.lastError)
{ alert("Disconnected. Reason: " + $.connection.hub.lastError.message); }
});