Websocket连接在Safari iOS

时间:2017-01-30 16:32:12

标签: javascript websocket webgl mobile-safari requestanimationframe

我对iOS设备上的websockets有一个相当奇怪和具体的问题。

我正在开发一个基于浏览器的Web应用程序,它通过websockets与Web服务器通信。对于客户端,使用浏览器的本地websocket,因此不涉及库(没有socket.io等)。

在服务器端,我使用带有ws module的Node.js作为websocket服务器。

在桌面浏览器和Android设备上一切正常,但在iOS上,这种连接常常会挂起"这意味着浏览器不会响应在websocket连接上收到的消息。 websocket" onmessage"处理程序没有被解雇。

然而,在调试服务器时,我可以清楚地看到消息如何离开服务器并被发送到浏览器,但是当出现此问题时,在Safari上没有任何反应。好像消息是"卡住"在设备的某个地方,因为如果我在GUI上触发另一个事件(例如移动滑块或点击按钮),那么" onmessage"立即执行。由于iOS上的所有浏览器共享相同的后端,因此会出现在Safari,Chrome和Firefox上。

我承认这些消息可能变得非常大,即它们可以变成大约100kb长。我读到一些websocket实现有这些大小的问题,所以我尝试在应用程序级别将它们分成几个块,但到目前为止还没有成功。

也许值得一提的是服务器操作系统是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 handler似乎存在问题。

我明天会更新我的发现。

1 个答案:

答案 0 :(得分:3)

如果通过requestAnimationFrame创建3D渲染循环并且同时应用程序使用Websocket的onmessage处理程序接收数据,则似乎会出现此问题。

解决方案是将requestAnimationFrame替换为setTimeout的交替调用(请参阅下面的示例),使用requestAnimationFrame模仿setTimeout或使用setInterval作为解决方法。

我在铬的问题跟踪器(herethere)中找到了这个错误的证据,因此我不知道这个错误与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