Gestire la disconnessione di un client in un Hub di ASP.NET SignalR

di Moreno Gentili, in ASP.NET 4.5, ASP.NET MVC, SignalR,

Nel corso dello speciale su ASP.NET SignalR, abbiamo visto come questa libreria ci consenta di creare una connessione persistente e bidirezionale tra client e server, usata per lo scambio di messaggi in real time.

Talvolta può capitare che la connessione venga persa, sia per motivi di rete fisica (es. cavo scollegato) che per momentanei disservizi da parte dell'ISP. Quando ciò accade, il client javascript di ASP.NET SignalR riesce in breve tempo ad accorgersi del problema e tenta immediatamente di effettuare una riconnessione al server. Il suo stato interno, memorizzato in $.connection.hub.state sotto forma di numero intero, cambierà da connected a reconnecting.
Possiamo intercettare il cambiamento di stato fornendo una callback a stateChanged, all'utile scopo di informare l'utente che non sta più ricevendo messaggi in real time.

$.connection.hub.stateChanged(function(state) {
  if (state.newState === $.signalR.connectionState.reconnecting) {
    //Visualizzo un avviso all'utente usando noty, un plugin di jQuery.
    noty({type:'warning',
      text:'La connessione con il server è stata persa ma ' + 
        è in atto un tentativo di riconnessione, attendi...'});
  }
});

La riconnessione

Il client javascript tenterà più volte di recuperare la connessione per un tempo massimo di 30 secondi. Questo è il default per DisconnectTimeout, un valore che possiamo personalizzare liberamente dal global.asax.

protected void Application_Start() 
{
  //Aumento il timeout a 40 secondi
  GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(40);
}

Il tentativo di riconnessione può concludersi in due modi che analizziamo di seguito.

Connessione recuperata
Il client javascript riesce a ricollegarsi al server e perciò mantiene lo stesso ConnectionId che gli era stato assegnato. Il suo stato interno, da reconnecting torna a connected.
Ogni messaggio generato durante la temporanea perdita di connessione viene immediatamente recapitato al client. ASP.NET SignalR, infatti, si avvale di un buffer interno che per default può conservare 1000 messaggi. Anche questo è un valore configurabile dal global.asax, agendo sulla proprietà DefaultMessageBufferSize dell'oggetto Configuration.

//Attenzione al consumo di RAM, specie se ogni messaggio pesa svariati kilobyte.
GlobalHost.Configuration.DefaultMessageBufferSize = 2000;

Contemporaneamente, nel nostro Hub viene invocato il metodo OnReconnected. Fare l'override di questo metodo ci dà un'opportunità di eseguire della logica lato server.

public override System.Threading.Tasks.Task OnReconnected() 
{
  Clients.Caller.SendMessage("Bentornato, la tua riconnessione ha avuto successo");
  return base.OnReconnected();
}

Disconnessione
Il client non riesce a recuperare la connessione prima dello scadere del DisconnectTimeout, a causa del persistere dei problemi di rete. Il suo stato passa da reconnecting a disconnected e tutti i messaggi giunti nel frattempo sono persi.
Il metodo OnDisconnected del nostro Hub viene invocato e, se stiamo realizzando un'applicazione di chat, potremo usarlo per informare gli altri client che l'utente si è disconnesso.

public override System.Threading.Tasks.Task OnDisconnected() 
{
  //invoco la rimozione del suo ConnectionId dalla lista di utenti attivi
  Clients.Others().RemoveUserFromList(Context.ConnectionId);
  return base.OnDisconnected();
}

Quando un client è ormai disconnesso, deve necessariamente effettuare una nuova connessione affinché possa ricevere ed inviare nuovi messaggi. Invocando $.connection.hub.start(), il server assegnerà al client un nuovo ConnectionId ed ogni eventuale messaggio di bootstrapping (es. nickname per l'accesso in chat) dovrà essere reinviato.

Questo diagramma riepiloga i 4 stati in cui può trovarsi il client javascript e mostra le possibili transizioni fra di essi.

Altre modalità di disconnessione

Finora abbiamo esaminato la disconnessione del client a causa di problemi di rete. Tuttavia, a volte l'utente potrebbe aver bisogno di disconnettersi volontariamente, o perché ha terminato la sua sessione di lavoro o perché vuole prendersi del tempo per esaminare un campione di dati che ha già ricevuto dal server.
Questa operazione è possibile in due modi:

  • Eseguendo il comando javascript $.connection.hub.stop() utile a consentire all'utente di arrestare il flusso di messaggi che arrivano dal server. La connessione potrà poi essere ristabilita con la funzione start.
  • Semplicemente chiudendo la pagina web: ASP.NET SignalR, infatti, gestisce l'evento window.unload della finestra del browser e perciò ha l'opportunità di inviare un comando abort al server. La connessione viene dunque chiusa correttamente anche nel caso in cui l'utente non effettui formalmente il logout.
  • Conclusioni

    Una connessione persistente, come qualsiasi altro tipo di connessione, può essere soggetta a interruzioni inaspettate o volontarie. Conoscere il comportamento di ASP.NET SignalR in queste situazioni ci aiuta a capire come realizzare una UI coerente con l'infrastruttura di comunicazione sottostante. Nei momenti in cui il client non è connesso al server, potremmo infatti inibire il bottone di invio dei messaggi e mostrare delle istruzioni all'utente affinché sappia cosa fare durante quella situazione non ottimale. Gestire i (seppur rari) casi imprevisti, renderà il software più affidabile perché in grado di supportare i suoi utenti anche nel corso di un malfunzionamento.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi