Socket.io: Safari abandona la conexión de socket web debido a la inactividad cuando la página no está enfocada

Creado en 25 abr. 2017  ·  26Comentarios  ·  Fuente: socketio/socket.io

Tú quieres:

  • [x] informar un error
  • [] solicitar una función

Comportamiento actual

No estoy seguro de si se trata de un problema conocido (intenté buscar pero no encontré nada). Safari para Mac parece estar desconectando silenciosamente las conexiones de websocket debido a la inactividad / inactividad si la página / pestaña no está enfocada.

Pasos para reproducir (si el comportamiento actual es un error)

Hacer que la pestaña / página de Safari no esté enfocada; registrar eventos de websocket.

Comportamiento esperado

Los Websockets deben mantenerse activos mediante la función de latido. Al no ver este comportamiento en otros navegadores, es poco probable que sea mi código.

Preparar

  • Sistema operativo: Mac OSX 10.12.4 (16E195)
  • navegador: Safari 10.1 (12603.1.30.0.34)
  • versión socket.io: 1.7.3

Otra información (por ejemplo, trazas de pila, problemas relacionados, sugerencias de cómo solucionarlo)

¿Es posible que se trate de algún tipo de función de ahorro de energía que anula / ignora los latidos del corazón?

bug

Comentario más útil

Creo que eso se debe a que la versión actual de socket.io se basa en setTimeout en el lado del cliente, que puede no ser tan confiable como se esperaba.

Incluiremos esto en la v3, ya que es un cambio importante.

Relacionado: https://github.com/primus/primus/issues/348

Todos 26 comentarios

El servidor en ejecución con DEBUG = * muestra lo siguiente:
socket.io:client client close with reason ping timeout +0ms
socket.io:socket closing socket - reason ping timeout +0ms

lo que supongo significa que Safari cerró la conexión, no el servidor de socket o la instancia del cliente. Sin embargo, lo más extraño es que he notado que Safari a veces se vuelve a conectar entre 30 segundos y 1 minuto más tarde, pero otras veces no lo hace y permanece desconectado hasta que la página se enfoca. Es increíblemente frustrante intentar depurar con un comportamiento tan inconsistente.

Parece que a veces se vuelve a conectar esporádicamente mucho más tarde (como 10 minutos). Nuevamente, completamente inconsistente en entornos de prueba idénticos.

@twistedpixel el retardo de reconexión es exponencial (es decir, algo como: espera 500 ms, intenta reconectarte, espera 1000 ms, intenta reconectarte ...) ( fuente ), así que eso puede explicar el comportamiento.

¿Qué tal forzar la reconexión cuando la ventana vuelve a enfocarse?

window.addEventListener("focus", () => socket.connect());

Puede estar relacionado con https://github.com/primus/primus/issues/348.

Gracias por la información, pero el problema principal es que necesito el conector web conectado permanentemente porque se usa para enviar alertas al usuario mientras está fuera. Por lo tanto, el enfoque de ventana para reconectarse no es ideal.

Creo que en realidad es algo más problemático específico para mi máquina / instalación de todos modos. Noté el comportamiento originalmente en mi iMac, así que decidí limpiar mi MacBook con una versión nueva de Safari y no veo el comportamiento allí en absoluto. Dejé una pestaña minimizada durante todo un día y no se desconectó ni una vez. Por lo tanto, intenté volver al iMac y eliminar todos los complementos de Internet y deshabilitar todas las extensiones, pero aún así vi este comportamiento.

Apple no parece proporcionar ninguna forma de reinstalar Safari por completo, aparte de eliminar sus preferencias y algunos otros archivos. O limpiando la máquina. Una parte de mí quiere comenzar de nuevo, pero el desarrollador que hay en mí odiaría no saber cuál es la causa.

En realidad, a su punto sobre las reconexiones exponenciales: seguramente la primera reconexión sería, como dice, alrededor de 500ms después de la desconexión ... entonces, ¿por qué el servidor lo ignoraría? Debe haber algo que le impida disparar la reconexión.

Es un poco extraño porque si coloco un socket.connect() en el evento de desconexión, se vuelve a conectar sin problemas. Tiene que hacerlo cada pocos minutos, pero aún así lo hace sin falta. ¡Así que estoy completamente desconcertado de por qué no está sucediendo una reconexión! Investigaré un poco más y veré si puedo averiguar por qué.

Este es el comportamiento típico del navegador hoy en día, incluso en computadoras de escritorio, desafortunadamente.

Creo que sé lo que está pasando. Safari es de hecho el problema.

Creo que todos los navegadores limitan los valores setTimeout y setInterval en 1000 cuando la pestaña no está enfocada. Safari, estúpidamente, lo limita a 1000 y hace algo como agregar exponencialmente un retraso que hace que cada iteración tome el doble de tiempo que la última. Es por eso que la conexión muere; Los tiempos de espera internos de socket.io se retrasan / eliminan, lo que explica por qué las reconexiones no ocurren cuando deberían.

Entonces, básicamente, Apple ha decidido ir contra la corriente, como de costumbre, lo que resulta en una mala experiencia de usuario. Son realmente buenos en esto estos días.

No he descubierto por qué está afectando al iMac y no al MacBook (hubiera esperado lo contrario) pero seguiré probando y veré si puedo identificar la razón exacta.

@twistedpixel no es solo Safari. Ver http://blog.strml.net/2017/01/chrome-56-now-aggressively-throttles.html

En Primus solucionamos el problema invirtiendo la dirección de los mensajes de latido (https://github.com/primus/primus/pull/534).

@lpinca Todo el tiempo que estuve tratando de resolver este problema me estaba preguntando eso mismo. Gracias por la info! He estado mirando Primus pero no quería tener que refactorizar todo mi código base tan pronto. Sin embargo, parece que podría valer la pena el esfuerzo.

@twistedpixel, mi punto es que probablemente se pueda hacer lo mismo en Engine.IO, por lo que no es necesario migrar a Primus.

FWIW, Safari Tech Preview no parece verse afectado por la limitación adicional. Quizás Apple revirtió su decisión. Todavía está acelerando a 1000ms, pero no parece agregar nada más.

Estoy experimentando el mismo problema en iOS 12 Safari. Si vuelvo a abrir mi safari, la conexión de websocket desaparecerá. ¿Existe una solución limpia para mantener vivo el socket?

AFAIK iOS Safari suspende ciertos procesos cuando Safari está en segundo plano (para evitar que se agote la batería) y las conexiones websocket son casi con certeza uno de esos procesos. Es poco probable que encuentre una solución alternativa en los dispositivos móviles.

OKAY. ¿Pero todavía puedo volver a conectarme si agrego un detector de eventos como onwindowfocus o algo así?

¿Alguien ha implementado una solución alternativa? estamos interesados ​​en buscar opciones y nos preguntamos si otros ya están experimentando

En lugar de utilizar eventos de enfoque, deberá utilizar la API de visibilidad de página para detectar cuándo se ha puesto en segundo plano la ventana de una aplicación móvil.

Me encontré con el problema con Azure SignalR y gracias a la sugerencia de @techpeace que actualmente usa la API de visibilidad de la página para cerrar la conexión en la ocultación de la página y volver a conectarla cuando la página es visible. Pero encontré problemas con el cambio rápido de pestañas, que puede enviar múltiples eventos. Actualmente, estoy buscando eliminar los rebotes de los eventos. Además, los consejos generales que se encuentran en la web desalientan cualquier tipo de manejo basado en la detección de agentes de usuario.

Soluciones

Probé durante varias horas con los 3 de estos navegadores, cambiando los valores pingTimeout & pingInterval . Lo que encontré como soluciones:

  1. Configuración pingTimeout > = 30000 ms

    • o -

  2. Configuración pingInterval <= 10000 ms

Creo que la mejor solución es cambiar pingTimeout = 30000 . El valor predeterminado pingInterval es 25000 ms y aumentar la frecuencia del servidor que hace ping a los clientes a cada 10 segundos puede ser demasiado hablador para proyectos _a escala_.

Creo que eso se debe a que la versión actual de socket.io se basa en setTimeout en el lado del cliente, que puede no ser tan confiable como se esperaba.

Incluiremos esto en la v3, ya que es un cambio importante.

Relacionado: https://github.com/primus/primus/issues/348

@darrachequesne también estoy en el móvil, si la pantalla de mi teléfono entra en modo de espera y abro el navegador de nuevo en el móvil, socket io desconecta el chat. por favor arregle esto. será un gran alivio.

alguna actualización sobre este error en socket io?

en mi aplicación cuando el usuario intenta cargar un archivo desde su navegador móvil, y cuando se abre el cuadro de diálogo de carga, socket io los desconecta si tardan 15 segundos o más en seleccionar un archivo.

si cambian a otra página o pestaña, después de 15 segundos, socket io los desconecta nuevamente, ¿hay alguna forma de solucionar esto y mantener vivo / conectado el socket io incluso si el usuario no está en la página / documento enfocado?

@darrachequesne

He solucionado este problema con la API de visibilidad.

Problema principal con Safari para mí: no tiene tiempo para cerrar el socket en visible.hidden === true, por lo que debe cerrar websocket después de desbloquear el dispositivo e iniciarlo nuevamente.

@ JustFly1984 ¿Tiene algún código de muestra para eso? Tengo la detección de visibilidad funcionando correctamente, pero no puedo volver a conectar el enchufe.

Así que ahora esto también está sucediendo con MacOS Safari, FYI.

@calendee @anilanar no estamos usando sockets.io, solo websockets puros, y también estamos usando React.js, por lo que el código es bastante complejo. la idea principal es que tenemos dos <ContextProvider /> para cada api, la visibilidad está en la parte superior, websockets en la parte inferior y websockets usando el contexto de la visibilidad.

Gracias por responderme JustFly1984. De hecho, al final, no necesitaba la API de visibilidad. Simplemente necesito agregar tiempos de espera. Una vez que hice esto, ya no tuve problemas de conexión en iOS Safari.

// Establish a Socket.io connection
// Initialize our Feathers client application through Socket.io
// with hooks and authentication.
client.configure(feathers.socketio(socket), {
  timeout: 2000,
});
// Use localStorage to store our login token
client.configure(feathers.authentication(), {
  timeout: 2000,
});
¿Fue útil esta página
0 / 5 - 0 calificaciones