Socket.io: Safari interrompant la connexion au socket Web en raison d'une inactivité lorsque la page n'est pas mise au point

Créé le 25 avr. 2017  ·  26Commentaires  ·  Source: socketio/socket.io

Tu veux:

  • [x] signaler un bogue
  • [] demander une fonctionnalité

Comportement actuel

Je ne sais pas s'il s'agit d'un problème connu (j'ai essayé de chercher mais je n'ai rien trouvé). Safari pour Mac semble abandonner silencieusement les connexions Websocket en raison de l'inactivité / de l'inactivité si la page / l'onglet n'est pas mis au point.

Étapes à reproduire (si le comportement actuel est un bogue)

Rendre l'onglet / page Safari non mis au point; consigner les événements Websocket.

Comportement attendu

Les Websockets doivent être maintenus en vie via la fonctionnalité Heartbeat. Ne pas voir ce comportement dans d'autres navigateurs est donc peu probable que ce soit mon code.

Installer

  • Système d'exploitation: Mac OSX 10.12.4 (16E195)
  • navigateur: Safari 10.1 (12603.1.30.0.34)
  • Version socket.io: 1.7.3

Autres informations (par exemple, traces de pile, problèmes associés, suggestions de résolution)

Est-ce peut-être une sorte de fonction d'économie d'énergie qui remplace / ignore les battements de cœur?

bug

Commentaire le plus utile

Je pense que c'est parce que la version actuelle de socket.io repose sur setTimeout côté client, qui n'est peut-être pas aussi fiable que prévu.

Nous l'inclurons dans la v3, car il s'agit d'un changement radical.

En relation: https://github.com/primus/primus/issues/348

Tous les 26 commentaires

Le serveur en cours d'exécution avec DEBUG = * montre ce qui suit:
socket.io:client client close with reason ping timeout +0ms
socket.io:socket closing socket - reason ping timeout +0ms

ce qui, je suppose, signifie que Safari a fermé la connexion, pas le serveur de socket ou l'instance client. Cependant, le plus étrange est que j'ai remarqué que Safari se reconnecte

Il semble que parfois, il se reconnecte sporadiquement beaucoup plus tard (comme 10 minutes). Encore une fois, complètement incohérent dans des environnements de test identiques.

@twistedpixel le délai de reconnexion est exponentiel (c'est-à-dire quelque chose comme: attendez 500 ms, essayez de vous reconnecter, attendez 1000 ms, essayez de vous reconnecter ...) ( source ), ce qui peut expliquer le comportement.

Que diriez-vous de forcer à se reconnecter lorsque la fenêtre obtient à nouveau le focus?

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

Il peut être lié à https://github.com/primus/primus/issues/348.

Merci pour l'information, mais le principal problème est que j'ai besoin que la prise Web soit connectée en permanence car elle est utilisée pour envoyer des alertes à l'utilisateur lorsqu'il est absent. Par conséquent, la mise au point de la fenêtre pour se reconnecter n'est pas idéale.

Je pense que c'est en fait quelque chose de plus problématique spécifique à ma machine / installation de toute façon. J'ai remarqué le comportement à l'origine sur mon iMac, alors j'ai décidé de simplement effacer mon MacBook avec une nouvelle version de Safari et je ne vois pas du tout le comportement. J'ai laissé un onglet minimisé pendant une journée entière et il ne s'est pas déconnecté une fois. J'ai donc essayé de revenir à l'iMac et de supprimer tous les plugins Internet et de désactiver toutes les extensions mais j'ai quand même vu ce comportement.

Apple ne semble pas fournir de moyen de réinstaller complètement Safari autre que la suppression de ses préférences et de certains autres fichiers. Ou essuyer la machine. Une partie de moi veut juste repartir à zéro, mais le développeur en moi détesterait ne pas savoir quelle est la cause.

En fait, pour revenir à votre point sur les reconnexions exponentielles: la première reconnexion serait sûrement, comme vous le dites, environ 500 ms après la déconnexion ... alors pourquoi le serveur l'ignorerait-il? Il doit y avoir quelque chose qui l'empêche de déclencher la reconnexion.

C'est un peu bizarre parce que si je colle un socket.connect() dans l'événement de déconnexion, il se connecte à nouveau très bien. Il doit le faire toutes les quelques minutes, mais il le fait toujours sans faute. Je suis donc complètement perplexe quant à la raison pour laquelle une reconnexion ne se produit pas! Je vais creuser un peu plus et voir si je peux comprendre pourquoi.

C'est le comportement typique des navigateurs de nos jours, même sur les ordinateurs de bureau, malheureusement.

Je pense que je sais ce qui se passe. Safari est en effet le problème.

Je pense que tous les navigateurs plafonnent les valeurs setTimeout et setInterval à 1000 lorsque l'onglet n'est pas au point. Safari - stupidement - le plafonne à 1000 et fait quelque chose comme l'ajout exponentiel d'un délai qui fait que chaque itération prend deux fois plus longtemps que la dernière. C'est pourquoi la connexion s'éteint; Les délais d'expiration internes de socket.io sont retardés / supprimés, expliquant pourquoi les reconnexions ne se produisent pas quand elles le devraient.

Donc, fondamentalement, Apple a décidé d'aller à contre-courant, comme d'habitude, ce qui se traduit par une mauvaise expérience utilisateur. Ils sont vraiment bons dans ce domaine ces jours-ci.

Je n'ai pas découvert pourquoi cela affecte l'iMac et non le MacBook (je m'attendais à l'inverse) mais je vais continuer à tester et voir si je peux identifier la raison exacte.

@twistedpixel ce n'est pas seulement Safari. Voir http://blog.strml.net/2017/01/chrome-56-now-aggressively-throttles.html

Dans Primus, nous avons contourné le problème en inversant la direction des messages de pulsation (https://github.com/primus/primus/pull/534).

@lpinca Tout le temps que j'essayais de posais la question. Merci pour l'info! J'ai regardé Primus mais je ne voulais pas avoir à refactoriser toute ma base de code si tôt. Il semble que cela en vaut la peine.

@twistedpixel mon point est que la même chose peut probablement être faite dans Engine.IO, il n'est donc pas nécessaire de migrer vers Primus.

FWIW, Safari Tech Preview ne semble pas être affecté par la limitation supplémentaire. Peut-être qu'Apple est revenu sur sa décision. Il est toujours limité à 1000 ms mais ne semble rien ajouter de plus.

Je rencontre le même problème sur iOS 12 Safari. Si je rouvre mon safari, la connexion Websocket a disparu. Existe-t-il une solution de contournement propre pour maintenir le socket en vie?

AFAIK iOS Safari suspend certains processus lorsque Safari est en arrière-plan (pour éviter l'épuisement de la batterie) et les connexions Websocket sont presque certainement l'un de ces processus. Il est peu probable que vous trouviez une solution de contournement sur les appareils mobiles.

D'ACCORD. Mais je peux toujours me reconnecter si j'ajoute un écouteur d'événement comme onwindowfocus ou quelque chose?

Quelqu'un a-t-il mis en œuvre une solution de contournement? nous sommes intéressés par des options et nous nous demandons si d'autres expérimentent déjà

Plutôt que d'utiliser des événements de focus, vous devrez utiliser l' API de visibilité de page pour détecter lorsqu'une fenêtre d'application mobile a été mise en arrière-plan.

Je suis tombé sur le problème avec Azure SignalR et grâce à la suggestion @techpeace utilisant actuellement l'API de visibilité de la page pour fermer la connexion sur la page cache et la reconnecter à nouveau lorsque la page est visible.Mais j'ai rencontré des problèmes avec le changement rapide d'onglets, ce qui peut envoyer plusieurs événements. Actuellement à la recherche de débouncer les événements .. De plus, les conseils généraux trouvés sur le Web découragent tout type de manipulation basée sur la détection de l'agent utilisateur ... ma solution est donc d'utiliser l'API de visibilité de page quel que soit l'agent utilisateur.

Solutions

J'ai testé pendant plusieurs heures avec ces 3 navigateurs, en changeant les valeurs pingTimeout & pingInterval . Ce que j'ai trouvé être des solutions:

  1. Réglage pingTimeout > = 30000 ms

    • ou -

  2. Réglage pingInterval <= 10000 ms

Je pense que la meilleure solution est de changer pingTimeout = 30000 . La valeur par défaut pingInterval est 25000 ms et augmenter la fréquence du serveur qui envoie un ping aux clients toutes les 10 secondes peut être trop bavard pour les projets _at scale_.

Je pense que c'est parce que la version actuelle de socket.io repose sur setTimeout côté client, qui n'est peut-être pas aussi fiable que prévu.

Nous l'inclurons dans la v3, car il s'agit d'un changement radical.

En relation: https://github.com/primus/primus/issues/348

@darrachequesne je suis également confronté à cela sur mobile si l'écran de mon téléphone passe en veille et que j'ouvre à nouveau le navigateur sur mobile, socket io déconnecte le chat. plz résoudre ce problème. ce sera un soulagement énorme.

une mise à jour sur ce bogue dans socket io?

dans mon application lorsque l'utilisateur tente de télécharger un fichier à partir de son navigateur mobile, et lorsque la boîte de dialogue de téléchargement s'ouvre, socket io les déconnecte s'ils mettent 15 secondes ou plus pour sélectionner un fichier.

s'ils basculent vers une autre page ou un autre onglet, après 15 secondes, socket io les déconnecte à nouveau, y a-t-il de toute façon pour résoudre ce problème et garder le socket io actif / connecté même si l'utilisateur n'est pas concentré sur la page / document?

@darrachequesne

J'ai résolu ce problème avec l'API Visibility.

Problème principal avec Safari pour moi - il n'a pas le temps de fermer le socket sur visible.hidden === true, vous devez donc fermer websocket après le déverrouillage de l'appareil et le relancer.

@ JustFly1984 Avez-vous un exemple de code pour cela. J'ai la détection de visibilité fonctionne correctement, mais je ne parviens pas à rétablir la connexion.

Alors maintenant, cela se produit également avec MacOS Safari, FYI.

@calendee @anilanar nous n'utilisons pas sockets.io, juste des websockets purs, et nous utilisons également React.js, donc le code est assez complexe. l'idée principale que nous avons deux <ContextProvider /> pour chaque api, la visibilité est en haut, les websockets en bas et les websockets utilisant le contexte de la visibilité.

Merci de me répondre JustFly1984. En fait, au final, je n'avais pas besoin de l'API de visibilité. J'ai simplement besoin d'ajouter des délais. Une fois que j'ai fait cela, je n'avais plus de problèmes de connexion sur 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,
});
Cette page vous a été utile?
0 / 5 - 0 notes