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:

Versioni software usate in questo argomento

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.

Diagramma dell'architettura di SignalR

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.

disconnessioni di trasporto

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.

Interruzioni del trasporto - Timeout del server

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'evento Reconnecting .

  • 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.

Errore del computer client

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.

Errore del server e riconnessione Se un server non diventa disponibile entro il periodo di timeout di disconnessione, la connessione SignalR termina. In questo scenario, l'evento "Closed" ("disconnesso" nei client JavaScript) viene generato nel client, ma "OnDisconnected" non viene mai chiamato nel server. Il diagramma seguente presuppone che l'API di trasporto non sia a conoscenza della connessione persa, quindi viene rilevata dalla funzionalità keepalive di SignalR e viene generato l'evento "ConnectionSlow".

Errore del server e timeout

Impostazioni di timeout e keepalive

I valori predefiniti ConnectionTimeout, DisconnectTimeoute 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.

Connessione di trasporto di polling lungo

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 il Start 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 al Stop 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); }
});