Socket.io: Safari разрывает соединение с веб-сокетом из-за бездействия, когда страница не в фокусе

Созданный на 25 апр. 2017  ·  26Комментарии  ·  Источник: socketio/socket.io

Вы хотите:

  • [x] сообщить об ошибке
  • [] запросить функцию

Текущее поведение

Не уверен, что это известная проблема (я пробовал искать, но ничего не нашел). Safari для Mac, по-видимому, незаметно отключает подключения к веб-сокетам из-за бездействия / бездействия, если страница / вкладка не в фокусе.

Действия по воспроизведению (если текущее поведение является ошибкой)

Сделать вкладку / страницу Safari не в фокусе; регистрировать события websocket.

Ожидаемое поведение

Веб-сокеты должны поддерживаться в рабочем состоянии с помощью функции Heartbeat. То, что я не вижу такого поведения в других браузерах, вряд ли будет моим кодом.

Настроить

  • ОС: Mac OSX 10.12.4 (16E195)
  • браузер: Safari 10.1 (12603.1.30.0.34)
  • версия socket.io: 1.7.3

Другая информация (например, трассировки стека, связанные проблемы, предложения по исправлению)

Возможно, это какая-то функция энергосбережения, которая переопределяет / игнорирует сердцебиение?

Самый полезный комментарий

Я думаю, это потому, что текущая версия socket.io полагается на setTimeout на стороне клиента, что может быть не так надежно, как ожидалось.

Мы включим это в v3, так как это критическое изменение.

Связанный: https://github.com/primus/primus/issues/348

Все 26 Комментарий

Запуск сервера с DEBUG = * показывает следующее:
socket.io:client client close with reason ping timeout +0ms
socket.io:socket closing socket - reason ping timeout +0ms

что, как я полагаю, означает, что Safari закрыл соединение, а не сервер сокета или экземпляр клиента. Однако, что самое странное, я заметил, что Safari иногда повторно подключается через 30 секунд или 1 минуту спустя, но в других случаях этого не происходит и остается отключенным, пока страница не будет переведена в фокус. Невероятно неприятно пытаться отладить такое непоследовательное поведение.

Кажется, что иногда он периодически переподключается гораздо позже (например, через 10 минут). Опять же, совершенно несовместимо в идентичных средах тестирования.

@twistedpixel задержка повторного подключения экспоненциальна (то есть что-то вроде: подождите 500 мс, попробуйте повторно подключиться, подождите 1000 мс, попробуйте повторно подключиться ...) ( источник ), так что это может объяснить поведение.

Как насчет принудительного повторного подключения, когда окно снова получает фокус?

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

Это может быть связано с https://github.com/primus/primus/issues/348.

Спасибо за информацию, но главная проблема в том, что мне нужен постоянно подключенный веб-сокет, потому что он используется для отправки предупреждений пользователю, пока он отсутствует. Поэтому фокус окна для повторного подключения не идеален.

Я думаю, что это на самом деле что-то более проблемное, специфичное для моей машины / установки. Первоначально я заметил такое поведение на моем iMac, поэтому решил просто стереть свой MacBook с помощью новой версии Safari, и я вообще не вижу там такого поведения. Я оставил вкладку свернутой на целый день, и она ни разу не отключилась. Поэтому я попытался вернуться к iMac, удалить все интернет-плагины и отключить все расширения, но все же увидел такое поведение.

Apple, похоже, не предлагает никакого способа полностью переустановить Safari, кроме удаления его настроек и некоторых других файлов. Или протирать машину. Часть меня хочет начать все сначала, но разработчик во мне не хотел бы знать, в чем причина.

На самом деле, к вашему вопросу об экспоненциальном повторном подключении: конечно, первое повторное подключение, как вы говорите, будет примерно через 500 мс после отключения ... так почему же сервер проигнорирует его? Должно быть что-то, что мешает ему запустить повторное подключение.

Это немного странно, потому что если я вставлю socket.connect() в событие отключения, он снова подключится нормально. Он должен делать это каждые несколько минут, но все же делает это в обязательном порядке. Так что я полностью озадачен, почему не происходит повторного подключения! Я покопаюсь еще немного и посмотрю, смогу ли я понять, почему.

В настоящее время это типичное поведение браузера, к сожалению, даже на настольных компьютерах.

Думаю, я знаю, что происходит. Safari - действительно проблема.

Я думаю, что все браузеры ограничивают значения setTimeout и setInterval значением 1000, когда вкладка не в фокусе. Safari - по глупости - ограничивает его значением 1000 и делает что-то вроде экспоненциального добавления задержки, в результате чего каждая итерация занимает вдвое больше времени, чем предыдущая. Вот почему связь умирает; Внутренние тайм-ауты socket.io задерживаются / сбрасываются, что объясняет, почему повторные подключения не происходят, когда они должны.

Таким образом, Apple, как обычно, решила пойти против течения, что привело к ухудшению пользовательского опыта. В наши дни они действительно хороши в этом.

Я не обнаружил, почему это влияет на iMac, а не на MacBook (я ожидал обратного), но я продолжу тестирование и посмотрю, смогу ли я определить точную причину.

@twistedpixel это не только Safari. См. Http://blog.strml.net/2017/01/chrome-56-now-aggressively-throttles.html

В Primus мы решили эту проблему, изменив направление сообщений пульса (https://github.com/primus/primus/pull/534).

@lpinca Все время, пока я пытался разобраться в этой проблеме, мне было интересно именно это. Спасибо за информацию! Я смотрел на Primus, но мне не хотелось так скоро рефакторинг всей моей кодовой базы. Кажется, это того стоит.

@twistedpixel, я

FWIW, Safari Tech Preview, похоже, не зависит от дополнительного регулирования. Возможно, Apple изменила свое решение. Он все еще дросселируется до 1000 мс, но, похоже, больше ничего не добавляет.

У меня такая же проблема с iOS 12 Safari. Если я снова открою сафари, соединение с веб-сокетом пропадет. Есть ли чистый обходной путь, чтобы сохранить сокет в рабочем состоянии?

AFAIK iOS Safari приостанавливает определенные процессы, когда Safari работает в фоновом режиме (для предотвращения разряда батареи), и подключения к веб-сокетам почти наверняка являются одним из таких процессов. Вряд ли вы найдете обходной путь на мобильных устройствах.

ХОРОШО. Но я все еще могу повторно подключиться, если добавлю прослушиватель событий, например onwindowfocus или что-то в этом роде?

Кто-нибудь реализовал обходной путь? мы заинтересованы в рассмотрении вариантов и задаемся вопросом, экспериментируют ли уже другие

Вместо использования событий фокуса вам нужно будет использовать API видимости страницы, чтобы определять, когда окно мобильного приложения было фоновым.

Я столкнулся с проблемой с Azure SignalR и благодаря предложению @techpeace, в настоящее время использующему API видимости страницы, чтобы закрыть соединение на скрытой странице и повторно подключить его снова, когда страница отображается. Но столкнулся с проблемами с быстрым переключением вкладок, которые могут отправлять несколько событий. В настоящее время изучается устранение событий ... Также общие рекомендации, найденные в Интернете, не рекомендуют никаких действий, основанных на обнаружении пользовательского агента ... поэтому мое решение состоит в том, чтобы использовать API видимости страницы независимо от пользовательского агента.

Решения

В течение нескольких часов я тестировал все 3 этих браузера, изменяя значения pingTimeout & pingInterval . Я нашел решения:

  1. Установка pingTimeout > = 30000 ms

    • или же -

  2. Установка pingInterval <= 10000 ms

Я считаю, что лучшим решением будет изменить pingTimeout = 30000 . Значение по умолчанию pingInterval равно 25000 мс, и увеличение частоты опроса клиентов на сервере каждые 10 секунд может быть слишком болтливым для проектов _at scale_.

Я думаю, это потому, что текущая версия socket.io полагается на setTimeout на стороне клиента, что может быть не так надежно, как ожидалось.

Мы включим это в v3, так как это критическое изменение.

Связанный: https://github.com/primus/primus/issues/348

@darrachequesne Я также столкнулся с тем, что на мобильном телефоне, если экран моего телефона переходит в режим ожидания и я снова открываю браузер на мобильном телефоне, socket io отключает чат. пожалуйста, исправьте это. это будет огромное облегчение.

какие-нибудь обновления по этой ошибке в socket io?

в моем приложении, когда пользователь пытается загрузить файл из своего мобильного браузера, и когда открывается диалоговое окно загрузки, socket io отключает их, если им требуется 15 секунд или более для выбора файла.

если они переключаются на другую страницу или вкладку, через 15 секунд сокет io снова отключает их, есть ли способ исправить это и сохранить сокет io активным / подключенным, даже если пользователь не находится на странице / документе?

@darrachequesne

Я исправил эту проблему с помощью Visibility API.

Основная проблема с Safari для меня - у него нет времени закрывать сокет на visible.hidden === true, поэтому вам нужно закрыть веб-сокет после того, как устройство разблокировано, и запустить его снова.

@ JustFly1984 У вас есть пример кода для этого. У меня обнаружение видимости работает правильно, но я не могу восстановить подключение к сокету.

Итак, теперь это происходит и с MacOS Safari, к вашему сведению.

@calendee @anilanar мы не используем sockets.io, только чистые веб-сокеты, а также мы используем React.js, поэтому код довольно сложный. основная идея состоит в том, что у нас есть два <ContextProvider /> для каждого api, видимость сверху, веб-сокеты внизу и веб-сокеты с использованием контекста из видимости.

Спасибо, что ответили мне JustFly1984. Собственно, в конце концов, API видимости мне не понадобился. Мне просто нужно добавить таймауты. Как только я это сделал, у меня больше не было проблем с подключением в 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,
});
Была ли эта страница полезной?
0 / 5 - 0 рейтинги