Descripción y control de eventos de duración de conexión en SignalR 1.x

de Patrick Fletcher, Tom Dykstra

Advertencia

Esta documentación no es para la última versión de SignalR. Eche un vistazo a SignalR de ASP.NET Core.

En este artículo se proporciona información general sobre los eventos de conexión, reconexión y desconexión de SignalR que puede gestionar, así como la configuración de tiempo de espera y mantenimiento de conexión que puede configurar.

En el artículo se asume que ya tiene algún conocimiento de SignalR y de los eventos de duración de la conexión. Para obtener una introducción a SignalR, consulte SignalR - Overview - Getting Started (Introducción a SignalR: introducción). Para obtener listas de eventos de duración de conexión, consulte los siguientes recursos:

Visión general

Este artículo contiene las siguientes secciones:

Los vínculos a temas de referencia de API son para la versión de .NET 4.5 de la API. Si usa .NET 4, consulte la versión de .NET 4 de los temas de api.

Terminología y escenarios de duración de la conexión

El OnReconnected controlador de eventos de un centro de SignalR se puede ejecutar directamente después de OnConnected, pero no después de OnDisconnected para un cliente determinado. La razón por la que puede tener una reconexión sin una desconexión es que hay varias maneras en las que se usa la palabra "conexión" en SignalR.

Conexiones de SignalR, conexiones de transporte y conexiones físicas

En este artículo se diferenciarán las conexiones de SignalR, las conexiones de transporte y las conexiones físicas:

  • La conexión de SignalR hace referencia a una relación lógica entre un cliente y una dirección URL de servidor, mantenida por signalR API y identificada de forma única por un identificador de conexión. SignalR mantiene los datos sobre esta relación y se usa para establecer una conexión de transporte. La relación finaliza y SignalR elimina los datos cuando el cliente llama al Stop método o se alcanza un límite de tiempo de espera mientras SignalR intenta restablecer una conexión de transporte perdida.
  • Conexión de transporte hace referencia a una relación lógica entre un cliente y un servidor, mantenida por una de las cuatro API de transporte: WebSockets, eventos enviados por el servidor, forever frame, o long polling. SignalR usa la API de transporte para crear una conexión de transporte y la API de transporte depende de la existencia de una conexión de red física para crear la conexión de transporte. La conexión de transporte finaliza cuando SignalR finaliza o cuando la API de transporte detecta que se interrumpe la conexión física.
  • La conexión física hace referencia a los vínculos de red físicos (cables, señales inalámbricas, enrutadores, etc.) que facilitan la comunicación entre un equipo cliente y un equipo servidor. La conexión física debe estar presente para establecer una conexión de transporte, y se debe establecer una conexión de transporte para establecer una conexión SignalR. Sin embargo, la interrupción de la conexión física no siempre finaliza inmediatamente la conexión de transporte o la conexión de SignalR, como se explica más adelante en este tema.

En el diagrama siguiente, la conexión de SignalR se representa mediante la API de Hubs y la capa PersistentConnection API SignalR, la conexión de transporte se representa mediante la capa Transports y la conexión física se representa mediante las líneas entre el servidor y los clientes.

Diagrama de arquitectura de SignalR

Cuando se llama al Start método en un cliente de SignalR, se proporciona código de cliente de SignalR con toda la información que necesita para establecer una conexión física a un servidor. El código de cliente de SignalR usa esta información para realizar una solicitud HTTP y establecer una conexión física que use uno de los cuatro métodos de transporte. Si se produce un error en la conexión de transporte o se produce un error en el servidor, la conexión de SignalR no desaparece inmediatamente porque el cliente sigue teniendo la información que necesita para volver a establecer automáticamente una nueva conexión de transporte a la misma dirección URL de SignalR. En este escenario, no interviene ninguna intervención de la aplicación de usuario y, cuando el código de cliente de SignalR establece una nueva conexión de transporte, no inicia una nueva conexión de SignalR. La continuidad de la conexión de SignalR se refleja en el hecho de que el identificador de conexión, que se crea al llamar al Start método, no cambia.

El OnReconnected controlador de eventos en el Hub se ejecuta cuando se restablece automáticamente una conexión de transporte después de haberse perdido. El OnDisconnected controlador de eventos se ejecuta al final de una conexión de SignalR. Una conexión de SignalR puede terminar de cualquiera de las maneras siguientes:

  • Si el cliente llama al Stop método , se envía un mensaje de detención al servidor y el cliente y el servidor finalizan inmediatamente la conexión de SignalR.
  • Después de perder la conectividad entre el cliente y el servidor, el cliente intenta volver a conectarse y el servidor espera a que el cliente se vuelva a conectar. Si los intentos de volver a conectarse no tienen éxito y finaliza el tiempo de espera antes de la desconexión, el cliente y el servidor terminan la conexión de SignalR. El cliente deja de intentar volver a conectarse y el servidor elimina su representación de la conexión signalR.
  • Si el cliente deja de ejecutarse sin tener la oportunidad de llamar al Stop método , el servidor espera a que el cliente se vuelva a conectar y, a continuación, finaliza la conexión de SignalR después del período de tiempo de espera de desconexión.
  • Si el servidor deja de ejecutarse, el cliente intenta volver a conectarse (volver a crear la conexión de transporte) y, a continuación, finaliza la conexión de SignalR después del período de tiempo de espera de desconexión.

Cuando no hay problemas de conexión y la aplicación de usuario finaliza la conexión de SignalR llamando al Stop método , la conexión de SignalR y la conexión de transporte comienzan y terminan al mismo tiempo. En las secciones siguientes se describen con más detalle los otros escenarios.

Escenarios de desconexión de transporte

Las conexiones físicas pueden ser lentas o puede haber interrupciones en la conectividad. En función de factores como la longitud de la interrupción, se podría quitar la conexión de transporte. SignalR intenta restablecer la conexión de transporte. A veces, la API de conexión de transporte detecta la interrupción y quita la conexión de transporte y SignalR descubre inmediatamente que se pierde la conexión. En otros escenarios, ni la API de conexión de transporte ni SignalR se enteran inmediatamente de que se ha perdido la conectividad. Para todos los transportes excepto el "long polling", el cliente de SignalR utiliza una función denominada keepalive para detectar pérdidas de conectividad que la API de transporte no puede detectar. Para obtener información sobre las conexiones de sondeo prolongado, consulte Configuración de tiempo de espera y mantenimiento más adelante en este tema.

Cuando una conexión está inactiva, el servidor envía periódicamente un paquete keepalive al cliente. A partir de la fecha en que se escribe este artículo, la frecuencia predeterminada es cada 10 segundos. Al escuchar estos paquetes, los clientes pueden indicar si hay un problema de conexión. Si no se recibe un paquete keepalive cuando se espera, después de un breve tiempo, el cliente asume que hay problemas de conexión, como la lentitud o las interrupciones. Si el keepalive sigue sin recibirse después de un tiempo más largo, el cliente asume que se ha quitado la conexión y comienza a intentar volver a conectarse.

En el diagrama siguiente se muestran los eventos de cliente y servidor que se generan en un escenario típico cuando hay problemas con la conexión física que la API de transporte no reconoce inmediatamente. El diagrama se aplica a las siguientes circunstancias:

  • El transporte es WebSockets, forever frame o eventos enviados por el servidor.
  • Hay distintos períodos de interrupción en la conexión de red física.
  • La API de transporte no conoce las interrupciones, por lo que SignalR se basa en la funcionalidad keepalive para detectarlas.

Desconexiones de transporte

Si el cliente entra en modo de reconexión, pero no puede establecer una conexión de transporte dentro del límite de tiempo de espera de desconexión, el servidor finaliza la conexión de SignalR. Cuando esto sucede, el servidor ejecuta el método del OnDisconnected concentrador y pone en cola un mensaje de desconexión para enviar al cliente en caso de que el cliente logre conectarse más adelante. Si el cliente vuelve a conectarse, recibe el comando de desconexión y llama al método Stop. En este escenario, OnReconnected no se ejecuta cuando el cliente se vuelve a conectar y OnDisconnected no se ejecuta cuando el cliente llama a Stop. El siguiente diagrama ilustra este escenario.

Interrupciones de transporte: tiempo de espera del servidor

Los eventos del ciclo de vida de la conexión de SignalR que pueden activarse en el cliente son los siguientes:

  • ConnectionSlow evento de cliente.

    Se genera cuando ha transcurrido una proporción preestablecida del período de espera de keepalive desde que se recibió el último mensaje o ping keepalive. El período de advertencia de tiempo de espera keepalive predeterminado es 2/3 del tiempo de espera keepalive. El tiempo de espera del mantenimiento activo es de 20 segundos, por lo que la advertencia se produce a aproximadamente 13 segundos.

    De forma predeterminada, el servidor envía pings keepalive cada 10 segundos, y el cliente verifica los pings keepalive aproximadamente cada 2 segundos (un tercio de la diferencia entre el valor de tiempo de espera keepalive y el valor de advertencia de tiempo de espera keepalive).

    Si la API de transporte detecta una desconexión, SignalR podría ser informado de la desconexión antes de que pase el período de advertencia del tiempo de espera de conexión activa. En ese caso, el evento ConnectionSlow no se generaría y SignalR iría directamente al evento Reconnecting.

  • Reconnecting evento de cliente.

    Se genera cuando (a) la API de transporte detecta que se pierde la conexión o (b) se ha superado el período de tiempo de espera de mantenimiento desde que se recibió el último mensaje o ping keepalive. El código de cliente de SignalR comienza a intentar volver a conectarse. Puede controlar este evento si desea que la aplicación realice alguna acción cuando se pierda una conexión de transporte. El período de tiempo de espera de mantenimiento predeterminado es actualmente de 20 segundos.

    Si el código de cliente intenta llamar a un método hub mientras SignalR está en modo de reconexión, SignalR intentará enviar el comando. La mayoría de las veces, estos intentos fracasarán, pero en algunas circunstancias podrían tener éxito. En el caso de los eventos enviados por el servidor, el marco para siempre y los transportes de sondeo largos, SignalR usa dos canales de comunicación, uno que el cliente usa para enviar mensajes y otro que usa para recibir mensajes. El canal utilizado para recibir es el abierto permanentemente y es el que se cierra cuando se interrumpe la conexión física. El canal usado para enviar permanece disponible, por lo que si se restaura la conectividad física, es posible que una llamada de método del cliente al servidor se realice correctamente antes de que se vuelva a establecer el canal de recepción. El valor devuelto no se recibiría hasta que SignalR vuelva a abrir el canal usado para recibir.

  • Reconnected evento de cliente.

    Se genera cuando se restablece la conexión de transporte. El OnReconnected controlador de eventos en el Hub se ejecuta.

  • Closed evento de cliente (disconnected evento en JavaScript).

    Se genera cuando expira el período de tiempo de espera de desconexión mientras el código de cliente de SignalR intenta volver a conectarse después de perder la conexión de transporte. El tiempo de espera de desconexión predeterminado es de 30 segundos. (Este evento también se genera cuando finaliza la conexión porque se llama al Stop método ).

Las interrupciones de la conexión de transporte que no detecta la API de transporte y que no retrasan la recepción de pings keepalive del servidor por más tiempo del período de advertencia del tiempo de espera de mantenimiento podrían no causar la generación de ningún evento relacionado con la duración de la conexión.

Algunos entornos de red cierran deliberadamente las conexiones inactivas y otra función de los paquetes keepalive es ayudar a evitar esto al permitir que estas redes sepan que hay una conexión de SignalR en uso. En casos extremos, es posible que la frecuencia predeterminada de pings keepalive no sea suficiente para evitar conexiones cerradas. En ese caso, puede configurar pings keepalive para que se envíen con más frecuencia. Para obtener más información, consulte Configuraciones de tiempo de espera y mantenimiento de la conexión más adelante en este tema.

Nota:

Important

No se garantiza la secuencia de eventos descritos aquí. SignalR realiza todos los intentos de generar eventos de duración de conexión de una manera predecible según este esquema, pero hay muchas variaciones de eventos de red y muchas maneras en las que los marcos de comunicaciones subyacentes, como las API de transporte, los controlan. Por ejemplo, es posible que el Reconnected evento no se genere cuando el cliente se vuelva a conectar o que el OnConnected controlador del servidor se ejecute cuando el intento de establecer una conexión no se realice correctamente. En este tema solo se describen los efectos que normalmente se producirían en determinadas circunstancias típicas.

Escenarios de desconexión de cliente

En un cliente del explorador, el código de cliente de SignalR que mantiene una conexión de SignalR se ejecuta en el contexto de JavaScript de una página web. Es por eso que la conexión de SignalR tiene que terminar al navegar de una página a otra y por eso tiene varias conexiones con varios identificadores de conexión si se conecta desde varias ventanas o pestañas del explorador. Cuando el usuario cierra una ventana o pestaña del explorador, o navega a una nueva página o actualiza la página, la conexión de SignalR finaliza inmediatamente porque el código de cliente de SignalR controla ese evento de explorador automáticamente y llama al Stop método . En estos escenarios, o en cualquier plataforma cliente cuando la aplicación llama al Stop método , el OnDisconnected controlador de eventos se ejecuta inmediatamente en el servidor y el cliente genera el Closed evento (el evento se denomina disconnected en JavaScript).

Si una aplicación cliente o el equipo en el que se ejecuta se bloquea o se va a suspender (por ejemplo, cuando el usuario cierra el portátil), el servidor no se informa de lo que ha ocurrido. En lo que respecta al servidor, la pérdida del cliente podría deberse a una interrupción de la conectividad y es posible que el cliente intente volver a conectarse. Por lo tanto, en estos escenarios, el servidor espera a que el cliente pueda volver a conectarse y OnDisconnected no se ejecuta hasta que expire el período de tiempo de espera de desconexión (unos 30 segundos de forma predeterminada). El siguiente diagrama ilustra este escenario.

Error del equipo cliente

Escenarios de desconexión del servidor

Cuando un servidor se desconecta, se reinicia, se produce un error, se recicla el dominio de la aplicación, etc., el resultado podría ser similar a una conexión perdida, o la API de transporte y SignalR podrían saber inmediatamente que el servidor ha desaparecido y SignalR podría empezar a intentar volver a conectarse sin generar el ConnectionSlow evento. Si el cliente entra en modo de reconexión y si el servidor recupera o reinicia o se pone en línea un nuevo servidor antes de que expire el período de tiempo de espera de desconexión, el cliente se volverá a conectar al servidor restaurado o nuevo. En ese caso, la conexión de SignalR continúa en el cliente y se genera el evento Reconnected. En el primer servidor, OnDisconnected nunca se ejecuta y, en el nuevo servidor, OnReconnected se ejecuta aunque OnConnected nunca se ejecutó para ese cliente en ese servidor antes. (El efecto es el mismo si el cliente se vuelve a conectar al mismo servidor después de un reinicio o reciclaje del dominio de la aplicación, porque cuando el servidor se reinicia no tiene memoria de actividad de conexión anterior). En el diagrama siguiente se supone que la API de transporte es consciente de la conexión perdida inmediatamente, por lo que no se genera el ConnectionSlow evento.

Error del servidor y reconexión

Si un servidor no está disponible dentro del período de tiempo de espera de desconexión, finaliza la conexión de SignalR. En este escenario, el Closed evento (disconnected en los clientes de JavaScript) se genera en el cliente, pero OnDisconnected nunca es invocado en el servidor. En el diagrama siguiente se supone que la API de transporte no es consciente de la conexión perdida, por lo que se detecta mediante la funcionalidad keepalive de SignalR y se genera el ConnectionSlow evento.

Error del servidor y tiempo de espera

Tiempo de espera y configuración de persistencia

Los valores predeterminados ConnectionTimeout, DisconnectTimeouty KeepAlive son adecuados para la mayoría de los escenarios, pero se pueden cambiar si el entorno tiene necesidades especiales. Por ejemplo, si el entorno de red cierra las conexiones que están inactivas durante 5 segundos, es posible que tenga que reducir el valor keepalive.

Tiempo de Espera de Conexión

Esta configuración representa la cantidad de tiempo para dejar abierta una conexión de transporte y esperar una respuesta antes de cerrarla y abrir una nueva conexión. El valor predeterminado es 110 segundos.

Esta configuración solo se aplica cuando la funcionalidad keepalive está deshabilitada, que normalmente solo se aplica al transporte de sondeo prolongado. En el diagrama siguiente se muestra el efecto de esta configuración en una conexión de transporte de sondeo largo.

Conexión de transporte de sondeo largo

TiempoDeDesconexión

Esta configuración representa la cantidad de tiempo que se debe esperar después de que se pierda una conexión de transporte antes de generar el Disconnected evento. El valor predeterminado es 30 segundos. Cuando se establece DisconnectTimeout, KeepAlive se establece automáticamente en 1/3 del DisconnectTimeout valor.

KeepAlive (mantener vivo)

Esta configuración representa la cantidad de tiempo que se debe esperar antes de enviar un paquete keepalive a través de una conexión inactiva. El valor predeterminado es 10 segundos. Este valor no debe ser superior a 1/3 del DisconnectTimeout valor.

Si desea establecer ambos DisconnectTimeout y KeepAlive, establezca KeepAlive después de DisconnectTimeout. De lo contrario KeepAlive , la configuración se sobrescribirá cuando DisconnectTimeout se establezca KeepAlive automáticamente en 1/3 del valor de tiempo de espera.

Si desea deshabilitar la funcionalidad de keepalive, establezca KeepAlive en null. La funcionalidad Keepalive se desactiva automáticamente para el transporte de long polling.

Cómo cambiar el tiempo de espera y la configuración keepalive

Para cambiar los valores predeterminados de esta configuración, establézcalos en el Application_Start archivo Global.asax , como se muestra en el ejemplo siguiente. Los valores que se muestran en el código de ejemplo son los mismos que los valores predeterminados.

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

Cómo notificar al usuario acerca de las desconexiones

En algunas aplicaciones, es posible que quiera mostrar un mensaje al usuario cuando haya problemas de conectividad. Tiene varias opciones para cómo y cuándo hacerlo. Los ejemplos de código siguientes son para un cliente de JavaScript mediante el proxy generado.

  • Controle el evento connectionSlow para mostrar un mensaje tan pronto como SignalR detecte problemas de conexión, antes de entrar en modo de reconexión.

    $.connection.hub.connectionSlow(function() {
        notifyUserOfConnectionProblem(); // Your function to notify user.
    });
    
  • Controle el reconnecting evento para mostrar un mensaje cuando SignalR sea consciente de una desconexión y vaya al modo de reconexión.

    $.connection.hub.reconnecting(function() {
        notifyUserOfTryingToReconnect(); // Your function to notify user.
    });
    
  • Controle el disconnected evento para mostrar un mensaje cuando se agote el tiempo de espera de un intento de reconexión. En este escenario, la única manera de volver a establecer una conexión con el servidor es reiniciar la conexión de SignalR llamando al Start método , que creará un nuevo identificador de conexión. En el siguiente ejemplo de código se utiliza un indicador para asegurarse de que solo se envía la notificación después de un tiempo de espera de reconexión, y no después de que la conexión de SignalR finalice normalmente debido a una llamada al método Stop.

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

Cómo volver a conectarse continuamente

En algunas aplicaciones, es posible que quiera volver a establecer automáticamente una conexión después de que se haya perdido y cuando el intento de reconexión haya agotado el tiempo de espera. Para ello, puede llamar al método Start desde su manejador de Closed eventos (disconnected manejador de eventos en clientes de JavaScript). Es posible que desee esperar un período de tiempo antes de llamar Start a para evitar hacerlo con demasiada frecuencia cuando el servidor o la conexión física no están disponibles. El ejemplo de código siguiente es para un cliente de JavaScript mediante el proxy generado.

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

Un posible problema que se debe tener en cuenta en los clientes móviles es que los intentos de reconexión continua cuando el servidor o la conexión física no están disponibles podrían provocar un drenaje innecesario de la batería.

Desconexión de un cliente en código de servidor

SignalR versión 1.1.1 no tiene una API de servidor integrada para desconectar clientes. Hay planes para agregar esta funcionalidad en el futuro. En la versión actual de SignalR, la manera más sencilla de desconectar un cliente del servidor es implementar un método de desconexión en el cliente y llamar a ese método desde el servidor. En el ejemplo de código siguiente se muestra un método de desconexión para un cliente de JavaScript mediante el proxy generado.

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

Advertencia

Seguridad: ni este método para desconectar clientes ni la API integrada propuesta abordarán el escenario de clientes hackeados que ejecutan código malintencionado, ya que los clientes podrían volver a conectarse o el código hackeado podría quitar el stopClient método o cambiar lo que hace. El lugar adecuado para implementar la protección contra denegación de servicio (DOS) con estado no está en el marco ni en la capa de servidor, sino en la infraestructura de front-end.