Websocket 连接在 Safari 中随机挂起 iOS
Websocket connection hangs randomly in Safari iOS
关于 iOS 设备上的 websockets,我有一个相当奇怪和具体的问题。
我正在开发一个基于浏览器的网络应用程序,它通过 websockets 与网络服务器通信。对于客户端,使用浏览器的本机 websocket,因此不涉及库(没有 socket.io 等)。
在服务器端,我使用 Node.js 和 ws module 作为 websocket 服务器。
在桌面浏览器和 Android 设备上一切正常,但在 iOS 上连接很频繁 "hangs",这意味着浏览器不响应在 websocket 连接上收到的消息. websocket 的 "onmessage" 处理程序不会被触发。
但是在调试服务器时,我可以清楚地看到消息是如何离开服务器并发送到浏览器的,但是当出现这个问题时,在 Safari 上没有任何反应。消息似乎在设备中的某处 "stuck",因为如果我在 GUI 上触发另一个事件(例如移动滑块或单击按钮),那么 "onmessage" 会立即执行。由于 iOS 上的所有浏览器共享相同的后端,因此在 Safari、Chrome 和 Firefox 上都会发生这种情况。
我承认消息可以变得非常大,即它们可以长到大约 100kb。我读到一些 websocket 实现有这些量级的问题,所以我尝试在应用程序级别将它们分成几个块,但到目前为止没有成功。
也许值得一提的是服务器 OS 是 Windows。
这是我简单的客户端代码(摘要):
var socket = new WebSocket("ws://myurl.com");
socket.onmessage = function(e) {
// Sometimes gets stuck before calling this handler.
// Can be resolved with triggering any event on the UI.
processData(e.data);
}
socket.onerror = function(e) {
logError(e);
}
socket.onclose = function(e) {
cleanUp();
}
服务器端看起来像这样:
var webServer = require("http")
.createServer()
.listen(80, "0.0.0.0", function () {
console.log("Listening on port " + 80);
});
var WebSocketServer = require("ws").Server;
var wss = new WebSocketServer({server: webServer});
wss.on("connection", function(ws) {
ws.on("message", function(message) {
processMessage(message);
});
});
到目前为止我尝试了什么
如前所述 here 使用内容安全策略(但
这可能更像是一个 Meteor / Cordova 问题)
在应用程序级别将消息拆分为块 (said here)
发送一些虚拟确认字节或禁用 Nagle 算法(按照建议here)
将 onmessage 回调包装到 setTimeout()
(here)
协议 WS 或 WSS 没有区别
运行 高速公路测试套件。有时它通过所有测试,有时其中一些失败可能是由于超时或执行时间太长
有人可以给我一些提示或对此类问题有经验吗?
更新 02.02.017
另外值得一提的是,我的应用程序是一个通过 requestAnimationFrame 循环渲染的 3D 应用程序。
经过多日的研究,我发现 requestAnimationFrame + WebSockets onmessage 处理程序似乎存在问题。
我明天会更新我的发现。
如果通过 requestAnimationFrame
创建 3D 渲染循环并且同时应用程序正在使用 Websocket 的 onmessage
处理程序接收数据,则似乎会出现此问题。
一个解决方案是用交替调用 setTimeout
代替 requestAnimationFrame
(参见下面的示例),用 setTimeout
模仿 requestAnimationFrame
或使用 setInterval
作为解决方法。
我在 chromium 的问题跟踪器 (here and there) 中找到了这个错误的证据,所以我不知道这个错误与 Apple 的 Webkit 有多大关系,但至少那里描述的症状非常相似并且解决方案/解决方法也对我有帮助。
下面我引用了这个 source 的解决方案,所有学分都转到评论 #30 那里:
var altframe = true, frametime = 0, lastframe = Date.now();
function Run() {
frametime = Date.now() - lastframe;
lastframe = Date.now();
if (!altframe)
setTimeout(Run,frametime);
// Hard-scheduled based on timing of last frame, must schedule before sim+render.
/*
SIM AND RENDER CODE
*/
if (altframe) {
window.requestAnimationFrame(Run);
// Normal timing, called after next paint.
altframe = false; // Alternate frame will be timed based on the next one.
}
else {
altframe = true; // Because we can't switch it above.
}
};
Run(); // As if we just had our setTimeout fire, altframe should be true
关于 iOS 设备上的 websockets,我有一个相当奇怪和具体的问题。
我正在开发一个基于浏览器的网络应用程序,它通过 websockets 与网络服务器通信。对于客户端,使用浏览器的本机 websocket,因此不涉及库(没有 socket.io 等)。
在服务器端,我使用 Node.js 和 ws module 作为 websocket 服务器。
在桌面浏览器和 Android 设备上一切正常,但在 iOS 上连接很频繁 "hangs",这意味着浏览器不响应在 websocket 连接上收到的消息. websocket 的 "onmessage" 处理程序不会被触发。
但是在调试服务器时,我可以清楚地看到消息是如何离开服务器并发送到浏览器的,但是当出现这个问题时,在 Safari 上没有任何反应。消息似乎在设备中的某处 "stuck",因为如果我在 GUI 上触发另一个事件(例如移动滑块或单击按钮),那么 "onmessage" 会立即执行。由于 iOS 上的所有浏览器共享相同的后端,因此在 Safari、Chrome 和 Firefox 上都会发生这种情况。
我承认消息可以变得非常大,即它们可以长到大约 100kb。我读到一些 websocket 实现有这些量级的问题,所以我尝试在应用程序级别将它们分成几个块,但到目前为止没有成功。
也许值得一提的是服务器 OS 是 Windows。
这是我简单的客户端代码(摘要):
var socket = new WebSocket("ws://myurl.com");
socket.onmessage = function(e) {
// Sometimes gets stuck before calling this handler.
// Can be resolved with triggering any event on the UI.
processData(e.data);
}
socket.onerror = function(e) {
logError(e);
}
socket.onclose = function(e) {
cleanUp();
}
服务器端看起来像这样:
var webServer = require("http")
.createServer()
.listen(80, "0.0.0.0", function () {
console.log("Listening on port " + 80);
});
var WebSocketServer = require("ws").Server;
var wss = new WebSocketServer({server: webServer});
wss.on("connection", function(ws) {
ws.on("message", function(message) {
processMessage(message);
});
});
到目前为止我尝试了什么
如前所述 here 使用内容安全策略(但 这可能更像是一个 Meteor / Cordova 问题)
在应用程序级别将消息拆分为块 (said here)
发送一些虚拟确认字节或禁用 Nagle 算法(按照建议here)
将 onmessage 回调包装到
setTimeout()
(here)协议 WS 或 WSS 没有区别
运行 高速公路测试套件。有时它通过所有测试,有时其中一些失败可能是由于超时或执行时间太长
有人可以给我一些提示或对此类问题有经验吗?
更新 02.02.017
另外值得一提的是,我的应用程序是一个通过 requestAnimationFrame 循环渲染的 3D 应用程序。
经过多日的研究,我发现 requestAnimationFrame + WebSockets onmessage 处理程序似乎存在问题。
我明天会更新我的发现。
如果通过 requestAnimationFrame
创建 3D 渲染循环并且同时应用程序正在使用 Websocket 的 onmessage
处理程序接收数据,则似乎会出现此问题。
一个解决方案是用交替调用 setTimeout
代替 requestAnimationFrame
(参见下面的示例),用 setTimeout
模仿 requestAnimationFrame
或使用 setInterval
作为解决方法。
我在 chromium 的问题跟踪器 (here and there) 中找到了这个错误的证据,所以我不知道这个错误与 Apple 的 Webkit 有多大关系,但至少那里描述的症状非常相似并且解决方案/解决方法也对我有帮助。
下面我引用了这个 source 的解决方案,所有学分都转到评论 #30 那里:
var altframe = true, frametime = 0, lastframe = Date.now();
function Run() {
frametime = Date.now() - lastframe;
lastframe = Date.now();
if (!altframe)
setTimeout(Run,frametime);
// Hard-scheduled based on timing of last frame, must schedule before sim+render.
/*
SIM AND RENDER CODE
*/
if (altframe) {
window.requestAnimationFrame(Run);
// Normal timing, called after next paint.
altframe = false; // Alternate frame will be timed based on the next one.
}
else {
altframe = true; // Because we can't switch it above.
}
};
Run(); // As if we just had our setTimeout fire, altframe should be true