Socket.io: 由于页面不在焦点时,由于不活动,Safari断开了Web套接字连接

创建于 2017-04-25  ·  26评论  ·  资料来源: socketio/socket.io

你想要:

  • [x]报告错误
  • []请求功能

当前行为

不知道这是否是一个已知问题(我尝试搜索但一无所获)。 Safari浏览器的Mac似乎被悄悄丢弃由于不活动/ WebSocket连接空闲如果页面/选项卡不在焦点。

重现步骤(如果当前行为是错误)

使Safari标签/页面不清晰; 记录websocket事件。

预期行为

Websocket应该通过心跳功能保持活动状态。 在其他浏览器中看不到此行为,因此不太可能成为我的代码。

设定

  • 作业系统:Mac OSX 10.12.4(16E195)
  • 浏览器:Safari 10.1(12603.1.30.0.34)
  • socket.io版本:1.7.3

其他信息(例如堆栈跟踪,相关问题,修复建议)

这是否可能是某种省电功能,可替代/忽略心跳?

最有用的评论

我认为这是因为socket.io的当前版本依赖于客户端的setTimeout,这可能不如预期的可靠。

我们将其包含在v3中,因为这是一项重大更改。

相关: https :

所有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有关

感谢您提供的信息,但主要的问题是我需要永久连接Web套接字,因为它用于在用户不在时向用户发送警报。 因此,重新聚焦的窗口焦点并不理想。

我认为这实际上是我的机器/安装特有的问题。 我注意到最初是在iMac上出现的问题,所以我决定只用新版本的Safari擦除MacBook,而我根本没有看到该问题。 我将标签最小化了一整天,它一次也没有断开。 因此,我尝试返回iMac并删除所有Internet插件并禁用所有扩展,但仍然看到了这种现象。

除了删除其首选项和某些其他文件外,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我的观点是,在Engine.IO中可能可以完成相同的操作,因此无需迁移到Primus。

FWIW,Safari Tech Preview似乎不受其他限制的影响。 也许苹果公司改变了他们的决定。 它仍在节流到1000毫秒,但似乎并没有增加任何东西。

我在iOS 12 Safari上遇到相同的问题。 如果我重新打开野生动物园,则websocket连接已消失。 有没有干净的解决方法来保持套接字存活?

当Safari进入后台(以防止电池耗尽)时,AFAIK iOS Safari会暂停某些进程,并且Websocket连接几乎肯定是这样的进程。 您不太可能在移动设备上找到解决方法。

好。 但是,如果我添加诸如onwindowfocus之类的事件侦听器,我仍然可以重新连接吗?

有没有人实施解决方法? 我们对寻找选择感兴趣,想知道其他人是否已经在尝试

您无需使用焦点事件,而需要使用Page Visibility API来检测移动应用程序窗口何时已被后台化。

我遇到了Azure SignalR的问题,并感谢@techpeace建议当前使用页面可见性API关闭页面隐藏上的连接并在页面可见时再次重新连接。但是遇到了快速切换选项卡的问题,该选项卡可以发送多个事件。 当前正在研究消除事件的可能性。.此外,在网络上发现的一般性建议也不鼓励基于用户代理检测的任何处理。因此,我的解决方案是使用页面可见性API,而与用户代理无关。

解决方案

我使用所有这三种浏览器测试了几个小时,更改了pingTimeoutpingInterval值。 我发现是解决方案:

  1. 设置pingTimeout > = 30000 ms

    • 要么 -

  2. 设置pingInterval <= 10000 ms

我相信最好的解决方案是更改pingTimeout = 30000 。 默认的pingInterval25000 ms毫秒,对于_at scale_项目而言,增加服务器将客户端ping客户端的频率每10s可能会太闲谈。

我认为这是因为socket.io的当前版本依赖于客户端的setTimeout,这可能不如预期的可靠。

我们将其包含在v3中,因为这是一项重大更改。

相关: https :

@darrachequesne我也可能在移动设备上遇到问题,如果我的手机屏幕处于待机状态,并且我再次在移动设备上打开浏览器,则io套接字会断开聊天。 请解决此问题。 这将是极大的缓解。

套接字io中此错误的任何更新?

在我的应用程序中,当用户尝试从其移动浏览器上载文件,并且在打开上载对话框时,如果套接字io需要15秒或更长时间来选择文件,则Socket io将其断开连接。

如果他们切换到另一个页面或选项卡,则在15秒钟后,套接字io再次将其断开连接,是否有办法解决此问题并保持套接字io处于活动状态/已连接,即使用户不在页面/文档上也没有关注?

@darrachequesne

我已使用Visibility API解决了此问题。

对我来说,Safari的主要问题-它没有时间关闭visible.hidden === true上的套接字,因此您需要在设备解锁后关闭websocket,然后重新启动它。

@ JustFly1984您是否有一些示例代码。 我已经可以看到可见性,但是我无法重新连接套接字。

所以现在这也发生在MacOS Safari中,仅供参考。

@calendee @anilanar我们不使用<ContextProvider /> ,可见性位于顶部,websockets位于底部,websockets使用可见性中的上下文。

多谢您回覆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 等级