Compartilhar via


Noções básicas e tratamento de eventos de tempo de vida de conexão no SignalR

Aviso

Esta documentação não é para a versão mais recente do SignalR. Dê uma olhada no ASP.NET Core SignalR.

Este artigo fornece uma visão geral dos eventos de conexão, reconexão e desconexão do SignalR que você pode manipular e configurações de tempo limite e keepalive que você pode definir.

O artigo pressupõe que você já tenha algum conhecimento do SignalR e dos eventos de tempo de vida da conexão. Para obter uma introdução ao SignalR, consulte Introdução ao SignalR. Para obter listas de eventos de tempo de vida da conexão, consulte os seguintes recursos:

Versões de software usadas neste tópico

Versões anteriores deste tópico

Para obter informações sobre versões anteriores do SignalR, consulte Versões mais antigas do SignalR.

Perguntas e comentários

Por favor, deixe comentários sobre como você gostou deste tutorial e o que poderíamos melhorar nos comentários na parte inferior da página. Se você tiver dúvidas que não estejam diretamente relacionadas ao tutorial, poderá postá-las no fórum ou StackOverflow.com do ASP.NET SignalR.

Visão geral

Este artigo inclui as seções a seguir:

Os links para tópicos de referência de API são para a versão .NET 4.5 da API. Se você estiver usando o .NET 4, consulte a versão do .NET 4 dos tópicos da API.

Terminologia e cenários de tempo de vida da conexão

O OnReconnected manipulador de eventos em um Hub do SignalR pode ser executado diretamente depois OnConnected , mas não depois, OnDisconnected para um determinado cliente. O motivo pelo qual você pode ter uma reconexão sem uma desconexão é que há várias maneiras pelas quais a palavra "conexão" é usada no SignalR.

Conexões do SignalR, conexões de transporte e conexões físicas

Este artigo diferenciará entre conexões do SignalR, conexões de transporte e conexões físicas:

  • A conexão do SignalR refere-se a uma relação lógica entre uma URL do cliente e do servidor, mantida pela API do SignalR e identificada exclusivamente por uma ID de conexão. Os dados sobre essa relação são mantidos pelo SignalR e são usados para estabelecer uma conexão de transporte. A relação termina e o SignalR descarta os dados quando o cliente chama o Stop método ou um limite de tempo limite é atingido enquanto o SignalR está tentando restabelecer uma conexão de transporte perdida.
  • A conexão de transporte refere-se a uma relação lógica entre um cliente e um servidor, mantida por uma das quatro APIs de transporte: WebSockets, eventos enviados pelo servidor, quadro permanente ou sondagem longa. O SignalR usa a API de transporte para criar uma conexão de transporte, e a API de transporte depende da existência de uma conexão de rede física para criar a conexão de transporte. A conexão de transporte termina quando o SignalR a encerra ou quando a API de transporte detecta que a conexão física foi interrompida.
  • A conexão física refere-se aos links de rede física - fios, sinais sem fio, roteadores, etc. - que facilitam a comunicação entre um computador cliente e um computador servidor. A conexão física deve estar presente para estabelecer uma conexão de transporte e uma conexão de transporte deve ser estabelecida para estabelecer uma conexão SignalR. No entanto, interromper a conexão física nem sempre encerra imediatamente a conexão de transporte ou a conexão do SignalR, como será explicado posteriormente neste tópico.

No diagrama a seguir, a conexão do SignalR é representada pela API de Hubs e pela camada SignalR da API PersistentConnection, a conexão de transporte é representada pela camada de Transportes e a conexão física é representada pelas linhas entre o servidor e os clientes.

Diagrama de arquitetura do SignalR

Ao chamar o método em um cliente SignalR, você está fornecendo ao código do Start cliente SignalR todas as informações necessárias para estabelecer uma conexão física com um servidor. O código do cliente do SignalR usa essas informações para fazer uma solicitação HTTP e estabelecer uma conexão física que usa um dos quatro métodos de transporte. Se a conexão de transporte falhar ou o servidor falhar, a conexão do SignalR não desaparecerá imediatamente porque o cliente ainda tem as informações necessárias para restabelecer automaticamente uma nova conexão de transporte com a mesma URL do SignalR. Nesse cenário, nenhuma intervenção do aplicativo do usuário está envolvida e, quando o código do cliente do SignalR estabelece uma nova conexão de transporte, ele não inicia uma nova conexão do SignalR. A continuidade da conexão do SignalR é refletida no fato de que a ID da conexão, que é criada quando você chama o Start método, não é alterada.

O OnReconnected manipulador de eventos no Hub é executado quando uma conexão de transporte é restabelecida automaticamente após ter sido perdida. O OnDisconnected manipulador de eventos é executado no final de uma conexão do SignalR. Uma conexão do SignalR pode terminar de qualquer uma das seguintes maneiras:

  • Se o cliente chamar o Stop método, uma mensagem de parada será enviada ao servidor e o cliente e o servidor encerrarão a conexão do SignalR imediatamente.
  • Depois que a conectividade entre o cliente e o servidor é perdida, o cliente tenta se reconectar e o servidor aguarda a reconexão do cliente. Se as tentativas de reconexão não forem bem-sucedidas e o período de tempo limite de desconexão terminar, o cliente e o servidor encerrarão a conexão do SignalR. O cliente para de tentar se reconectar e o servidor descarta sua representação da conexão do SignalR.
  • Se o cliente parar de ser executado sem ter a chance de chamar o Stop método, o servidor aguardará a reconexão do cliente e, em seguida, encerrará a conexão do SignalR após o período de tempo limite de desconexão.
  • Se o servidor parar de funcionar, o cliente tentará se reconectar (recriar a conexão de transporte) e, em seguida, encerrará a conexão do SignalR após o período de tempo limite de desconexão.

Quando não há problemas de conexão e o aplicativo do usuário encerra a conexão do SignalR chamando o Stop método, a conexão do SignalR e a conexão de transporte começam e terminam quase ao mesmo tempo. As seções a seguir descrevem com mais detalhes os outros cenários.

Cenários de desconexão de transporte

As conexões físicas podem ser lentas ou pode haver interrupções na conectividade. Dependendo de fatores como a duração da interrupção, a conexão de transporte pode ser interrompida. Em seguida, o SignalR tenta restabelecer a conexão de transporte. Às vezes, a API de conexão de transporte detecta a interrupção e descarta a conexão de transporte, e o SignalR descobre imediatamente que a conexão foi perdida. Em outros cenários, nem a API de conexão de transporte nem o SignalR ficam cientes imediatamente de que a conectividade foi perdida. Para todos os transportes, exceto sondagem longa, o cliente SignalR usa uma função chamada keepalive para verificar se há perda de conectividade que a API de transporte não consegue detectar. Para obter informações sobre conexões de sondagem longas, consulte Configurações de tempo limite e keepalive mais adiante neste tópico.

Quando uma conexão está inativa, periodicamente o servidor envia um pacote keepalive para o cliente. A partir da data em que este artigo está sendo escrito, a frequência padrão é a cada 10 segundos. Ao ouvir esses pacotes, os clientes podem dizer se há um problema de conexão. Se um pacote keepalive não for recebido quando esperado, após um curto período de tempo, o cliente assumirá que há problemas de conexão, como lentidão ou interrupções. Se o keepalive ainda não for recebido após um longo tempo, o cliente assumirá que a conexão foi descartada e começará a tentar se reconectar.

O diagrama a seguir ilustra os eventos de cliente e servidor que são gerados em um cenário típico quando há problemas com a conexão física que não são imediatamente reconhecidos pela API de transporte. O diagrama se aplica às seguintes circunstâncias:

  • O transporte é WebSockets, quadro permanente ou eventos enviados pelo servidor.
  • Existem vários períodos de interrupção na conexão de rede física.
  • A API de transporte não fica ciente das interrupções, portanto, o SignalR depende da funcionalidade keepalive para detectá-las.

as desconexões de transporte

Se o cliente entrar no modo de reconexão, mas não puder estabelecer uma conexão de transporte dentro do limite de tempo limite de desconexão, o servidor encerrará a conexão do SignalR. Quando isso acontece, o servidor executa o método do OnDisconnected Hub e enfileira uma mensagem de desconexão para enviar ao cliente caso o cliente consiga se conectar mais tarde. Se o cliente se reconectar, ele receberá o comando disconnect e chamará o Stop método. Nesse cenário, OnReconnected não é executado quando o cliente se reconecta e OnDisconnected não é executado quando o cliente chama Stop. O diagrama a seguir ilustra esse cenário.

Interrupções de transporte - tempo limite do servidor

Os eventos de tempo de vida da conexão do SignalR que podem ser gerados no cliente são os seguintes:

  • ConnectionSlow evento do cliente.

    Gerado quando uma proporção predefinida do período de tempo limite de keepalive passou desde que a última mensagem ou ping de keepalive foi recebido. O período de aviso de tempo limite de keepalive padrão é 2/3 do tempo limite de keepalive. O tempo limite de manutenção de atividade é de 20 segundos, portanto, o aviso ocorre em cerca de 13 segundos.

    Por padrão, o servidor envia pings de keepalive a cada 10 segundos e o cliente verifica se há pings de keepalive a cada 2 segundos (um terço da diferença entre o valor de tempo limite de keepalive e o valor de aviso de tempo limite de keepalive).

    Se a API de transporte tomar conhecimento de uma desconexão, o SignalR poderá ser informado da desconexão antes que o período de aviso de tempo limite de keepalive passe. Nesse caso, o evento não seria gerado e o ConnectionSlow SignalR iria diretamente para o Reconnecting evento.

  • Reconnecting evento do cliente.

    Gerado quando (a) a API de transporte detecta que a conexão foi perdida ou (b) o período de tempo limite de keepalive passou desde que a última mensagem ou ping de keepalive foi recebido. O código do cliente do SignalR começa a tentar se reconectar. Você pode manipular esse evento se quiser que seu aplicativo execute alguma ação quando uma conexão de transporte for perdida. O período de tempo limite de keepalive padrão é atualmente de 20 segundos.

    Se o código do cliente tentar chamar um método Hub enquanto o SignalR estiver no modo de reconexão, o SignalR tentará enviar o comando. Na maioria das vezes, essas tentativas falharão, mas em algumas circunstâncias podem ser bem-sucedidas. Para os eventos enviados pelo servidor, quadro para sempre e transportes de sondagem longos, o SignalR usa dois canais de comunicação, um que o cliente usa para enviar mensagens e outro que ele usa para receber mensagens. O canal usado para receber é o permanentemente aberto, e é aquele que é fechado quando a conexão física é interrompida. O canal usado para envio permanece disponível, portanto, se a conectividade física for restaurada, uma chamada de método do cliente para o servidor poderá ser bem-sucedida antes que o canal de recebimento seja restabelecido. O valor retornado não seria recebido até que o SignalR reabrisse o canal usado para recebimento.

  • Reconnected evento do cliente.

    Levantado quando a conexão de transporte é restabelecida. O OnReconnected manipulador de eventos no Hub é executado.

  • Closed evento client (disconnected evento em JavaScript).

    Gerado quando o período de tempo limite de desconexão expira enquanto o código do cliente do SignalR está tentando se reconectar depois de perder a conexão de transporte. O tempo limite de desconexão padrão é de 30 segundos. (Esse evento também é gerado quando a conexão termina porque o Stop método é chamado.)

As interrupções de conexão de transporte que não são detectadas pela API de transporte e não atrasam o recebimento de pings de keepalive do servidor por mais tempo do que o período de aviso de tempo limite de keepalive podem não fazer com que nenhum evento de tempo de vida de conexão seja gerado.

Alguns ambientes de rede fecham deliberadamente conexões ociosas e outra função dos pacotes keepalive é ajudar a evitar isso, permitindo que essas redes saibam que uma conexão SignalR está em uso. Em casos extremos, a frequência padrão de pings de keepalive pode não ser suficiente para evitar conexões fechadas. Nesse caso, você pode configurar pings de keepalive para serem enviados com mais frequência. Para obter mais informações, consulte Configurações de tempo limite e keepalive mais adiante neste tópico.

Observação

Importante: A sequência de eventos descrita aqui não é garantida. O SignalR faz todas as tentativas de gerar eventos de tempo de vida de conexão de maneira previsível de acordo com esse esquema, mas há muitas variações de eventos de rede e muitas maneiras pelas quais as estruturas de comunicação subjacentes, como APIs de transporte, lidam com eles. Por exemplo, o Reconnected evento pode não ser gerado quando o cliente se reconecta ou o OnConnected manipulador no servidor pode ser executado quando a tentativa de estabelecer uma conexão não é bem-sucedida. Este tópico descreve apenas os efeitos que normalmente seriam produzidos por certas circunstâncias típicas.

Cenários de desconexão do cliente

Em um cliente de navegador, o código do cliente do SignalR que mantém uma conexão do SignalR é executado no contexto JavaScript de uma página da Web. É por isso que a conexão do SignalR deve terminar quando você navega de uma página para outra, e é por isso que você tem várias conexões com várias IDs de conexão se você se conectar de várias janelas ou guias do navegador. Quando o usuário fecha uma janela ou guia do navegador, navega para uma nova página ou atualiza a página, a conexão do SignalR termina imediatamente porque o código do cliente do SignalR manipula esse evento do navegador para você e chama o Stop método. Nesses cenários, ou em qualquer plataforma de cliente quando seu aplicativo chama o Stop método, o OnDisconnected manipulador de eventos é executado imediatamente no servidor e o cliente gera o Closed evento (o evento é nomeado disconnected em JavaScript).

Se um aplicativo cliente ou o computador em que ele está sendo executado falhar ou entrar em suspensão (por exemplo, quando o usuário fechar o laptop), o servidor não será informado sobre o que aconteceu. Até onde o servidor sabe, a perda do cliente pode ser devido à interrupção da conectividade e o cliente pode estar tentando se reconectar. Portanto, nesses cenários, o servidor aguarda para dar ao cliente a chance de se reconectar e OnDisconnected não é executado até que o período de tempo limite de desconexão expire (cerca de 30 segundos por padrão). O diagrama a seguir ilustra esse cenário.

Falha no computador cliente

Cenários de desconexão do servidor

Quando um servidor fica offline – ele é reinicializado, falha, o domínio do aplicativo é reciclado etc. – o resultado pode ser semelhante a uma conexão perdida ou a API de transporte e o SignalR podem saber imediatamente que o servidor desapareceu e o SignalR pode começar a tentar se reconectar sem gerar o ConnectionSlow evento. Se o cliente entrar no modo de reconexão e se o servidor se recuperar ou reiniciar ou um novo servidor for colocado online antes que o período de tempo limite de desconexão expire, o cliente se reconectará ao servidor restaurado ou novo. Nesse caso, a conexão do SignalR continua no cliente e o Reconnected evento é gerado. No primeiro servidor, OnDisconnected nunca é executado, e no novo servidor, é executado, OnReconnected embora OnConnected nunca tenha sido executado para esse cliente nesse servidor antes. (O efeito é o mesmo se o cliente se reconectar ao mesmo servidor após uma reinicialização ou reciclagem de domínio de aplicativo, pois quando o servidor é reiniciado, ele não tem memória da atividade de conexão anterior.) O diagrama a seguir pressupõe que a API de transporte tome conhecimento da conexão perdida imediatamente, portanto, o ConnectionSlow evento não é gerado.

Falha e reconexão do servidor Se um servidor não ficar disponível dentro do período de tempo limite de desconexão, a conexão do SignalR será encerrada. Nesse cenário, o evento 'Closed' ('disconnected' em clientes JavaScript) é gerado no cliente, mas 'OnDisconnected' nunca é chamado no servidor. O diagrama a seguir pressupõe que a API de transporte não toma conhecimento da conexão perdida, portanto, ela é detectada pela funcionalidade de keepalive do SignalR e o evento 'ConnectionSlow' é gerado.

Falha e tempo limite do servidor

Configurações de tempo limite e keepalive

Os valores padrão ConnectionTimeout, , e KeepAlive são apropriados para a maioria dos cenários, DisconnectTimeoutmas podem ser alterados se o ambiente tiver necessidades especiais. Por exemplo, se o ambiente de rede fechar conexões ociosas por 5 segundos, talvez seja necessário diminuir o valor de keepalive.

ConnectionTimeout

Essa configuração representa a quantidade de tempo para deixar uma conexão de transporte aberta e aguardando uma resposta antes de fechá-la e abrir uma nova conexão. O valor padrão é 110 segundos.

Essa configuração se aplica somente quando a funcionalidade keepalive está desabilitada, o que normalmente se aplica apenas ao transporte de sondagem longo. O diagrama a seguir ilustra o efeito dessa configuração em uma conexão de transporte de sondagem longa.

Conexão de transporte de sondagem longa

DesconectarTempo limite

Essa configuração representa a quantidade de tempo de espera após a perda de uma conexão de transporte antes de gerar o Disconnected evento. O valor padrão é 30 segundos. Quando você define DisconnectTimeout, KeepAlive é automaticamente definido como 1/3 do DisconnectTimeout valor.

KeepAlive

Essa configuração representa a quantidade de tempo de espera antes de enviar um pacote keepalive por uma conexão ociosa. O valor padrão é 10 segundos. Esse valor não deve ser superior a 1/3 do DisconnectTimeout valor.

Se você quiser definir ambos DisconnectTimeout e KeepAlive, definir KeepAlive após DisconnectTimeout. Caso contrário, sua KeepAlive configuração será substituída quando DisconnectTimeout definida KeepAlive automaticamente como 1/3 do valor de tempo limite.

Se você quiser desabilitar a funcionalidade keepalive, defina KeepAlive como null. A funcionalidade de manutenção de atividade é desativada automaticamente para o transporte de sondagem longo.

Como alterar as configurações de tempo limite e keepalive

Para alterar os valores padrão dessas configurações, defina-os no Application_Start arquivo Global.asax , conforme mostrado no exemplo a seguir. Os valores mostrados no código de exemplo são os mesmos que os valores padrão.

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();
}

Como notificar o usuário sobre desconexões

Em alguns aplicativos, talvez você queira exibir uma mensagem para o usuário quando houver problemas de conectividade. Você tem várias opções de como e quando fazer isso. Os exemplos de código a seguir são para um cliente JavaScript usando o proxy gerado.

  • Manipule o evento para exibir uma mensagem assim que o connectionSlow SignalR estiver ciente dos problemas de conexão, antes de entrar no modo de reconexão.

    $.connection.hub.connectionSlow(function() {
        notifyUserOfConnectionProblem(); // Your function to notify user.
    });
    
  • Manipule o evento para exibir uma mensagem quando o reconnecting SignalR estiver ciente de uma desconexão e estiver entrando no modo de reconexão.

    $.connection.hub.reconnecting(function() {
        notifyUserOfTryingToReconnect(); // Your function to notify user.
    });
    
  • Manipule o disconnected evento para exibir uma mensagem quando uma tentativa de reconexão atingir o tempo limite. Nesse cenário, a única maneira de restabelecer uma conexão com o servidor novamente é reiniciar a conexão do SignalR chamando o Start método, que criará uma nova ID de conexão. O exemplo de código a seguir usa um sinalizador para garantir que você emita a notificação somente após um tempo limite de reconexão, não após um término normal da conexão do SignalR causada pela chamada do Stop método.

    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.
        }
    });
    

Como se reconectar continuamente

Em alguns aplicativos, você pode querer restabelecer automaticamente uma conexão depois que ela for perdida e a tentativa de reconexão expirar. Para fazer isso, você pode chamar o Start método do manipulador Closed de eventos (disconnected manipulador de eventos em clientes JavaScript). Talvez você queira aguardar um período de tempo antes de ligar Start para evitar fazer isso com muita frequência quando o servidor ou a conexão física não estiverem disponíveis. O exemplo de código a seguir é para um cliente JavaScript que usa o proxy gerado.

$.connection.hub.disconnected(function() {
   setTimeout(function() {
       $.connection.hub.start();
   }, 5000); // Restart connection after 5 seconds.
});

Um possível problema a ser observado em clientes móveis é que as tentativas contínuas de reconexão quando o servidor ou a conexão física não estão disponíveis podem causar consumo desnecessário de bateria.

Como desconectar um cliente no código do servidor

O SignalR versão 2 não tem uma API de servidor interna para desconectar clientes. Há planos para adicionar essa funcionalidade no futuro. Na versão atual do SignalR, a maneira mais simples de desconectar um cliente do servidor é implementar um método de desconexão no cliente e chamar esse método do servidor. O exemplo de código a seguir mostra um método disconnect para um cliente JavaScript usando o proxy gerado.

var myHubProxy = $.connection.myHub
myHubProxy.client.stopClient = function() {
    $.connection.hub.stop();
};

Aviso

Segurança – nem esse método para desconectar clientes nem a API interna proposta abordarão o cenário de clientes invadidos que estão executando código mal-intencionado, pois os clientes podem se reconectar ou o código invadido pode remover o método ou alterar o stopClient que ele faz. O local apropriado para implementar a proteção de negação de serviço (DOS) com estado não é na estrutura ou na camada do servidor, mas sim na infraestrutura de front-end.

Detectando o motivo de uma desconexão

O SignalR 2.1 adiciona uma sobrecarga ao evento do servidor OnDisconnect que indica se o cliente se desconectou deliberadamente em vez de atingir o tempo limite. O StopCalled parâmetro será true se o cliente fechou explicitamente a conexão. Em JavaScript, se um erro do servidor levou o cliente a se desconectar, as informações do erro serão passadas para o cliente como $.connection.hub.lastError.

Código do servidor C#: stopCalled parâmetro

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);
}

Código do cliente JavaScript: acessando lastError no disconnect evento.

$.connection.hub.disconnected(function () {
    if ($.connection.hub.lastError) 
        { alert("Disconnected. Reason: " +  $.connection.hub.lastError.message); }
});