提问者:小点点

Websocket 连接在 Safari iOS 中随机挂起


关于iOS设备上的网络套接字,我有一个相当奇怪和具体的问题。

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

在服务器端,我使用Node.js和ws模块作为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);
    });
});

>

  • 玩弄这里提到的内容安全策略(但这可能更多是Meteor/Cordova问题)

    在应用程序级别将消息拆分为块(这里说)

    发送一些伪确认字节或禁用Nagle算法(如这里所建议的)

    将onmessage回调包装到< code>setTimeout()(此处)

    协议WS或WSS没有区别

    运行Autobahn测试套件。有时它通过了所有测试,有时有些测试可能由于超时或执行时间过长而失败

    有人可以给我一些提示或有这类问题的经验吗?

    更新02.02.017

    另外值得一提的是,我的应用程序是一个通过requestAnimationFrame循环渲染的3D应用程序。

    经过几天的研究,我发现消息处理程序上的requestAnimationFrame WebSockets似乎存在问题。

    我明天会更新我的发现。


  • 共1个答案

    匿名用户

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

    一种解决方案是将< code > requestAnimationFrame 替换为< code>setTimeout的交替调用(参见下面的示例),用< code>setTimeout模仿< code > requestAnimationFrame ,或者使用< code>setInterval作为解决方法。

    我在chromium的问题跟踪器(这里和那里)中找到了这种 bug 的证据,所以我不知道这种 bug 与苹果的Webkit有多大关系,但至少那里描述的症状非常相似,解决方案/变通办法也帮助了我。

    下面我引用了这个来源的解决方案,所有的功劳都在这里的第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