iOS - Websocket 关闭和错误事件未触发

Thi*_*ibs 7 safari web-applications mobile-safari websocket ios

我们注意到当 Websocket 连接丢失时 Safari iOS 不调用 Websocket 事件的问题。我们的 Web 应用程序不知道 Websocket 的连接已丢失。在 Android 设备上,一旦连接断开,就会触发 close 和 error Websocket 事件。

我们创建了一个简单的示例。

NodeJS 中的 Websocket 服务器

const WebSocket = require('ws');
const wss = new WebSocket.Server({port: 8080});
wss.on('connection', function connection(ws) {
    ws.on('message', function incoming(message) {
        ws.send(`You sent: ${message}`);
    });
    ws.on('close', function close() {
        console.log('Client has disconnected');
    });
});
Run Code Online (Sandbox Code Playgroud)

简单客户端

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket Example</title>
</head>
<body>
<h1>WebSocket Example</h1>
<div id="output"></div>
<form>
    <label>
        Message:
        <input type="text" id="message">
    </label>
    <button type="submit" id="send">Send</button>
</form>
<script>
    const output = document.getElementById('output');
    const messageInput = document.getElementById('message');
    const sendButton = document.getElementById('send');
    const ws = new WebSocket('ws://localhost:8080');
       
    ws.addEventListener('open', function (event) {
        console.log((new Date()).toISOString(), '********************** OPEN **********************');
    });

    ws.addEventListener('close', function (event) {
        console.log((new Date()).toISOString(), '********************** CLOSE **********************');
    });

    ws.addEventListener('error', function (event) {
        console.log((new Date()).toISOString(), '********************** ERROR **********************');
    });

    ws.addEventListener('message', function (event) {
        console.log((new Date()).toISOString(), '********************** MESSAGE **********************');

        // Append the message to the output div
        const message = document.createElement('p');
        message.textContent = event.data;
        output.appendChild(message);
    });

    sendButton.addEventListener('click', function (event) {
        event.preventDefault();

        const message = messageInput.value;

        // Send the message to the server
        ws.send(message);
    });
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

当上述代码运行时,iOS 移动 Safari 不会触发事件closeerrorWebsocket 连接关闭时。

关闭 Websocket 的示例有:

  • 将设备置于飞行模式
  • 关闭 wifi 路由器电源
  • 关闭设备的 WiFi

正如之前提到的,这在 Android 和其他设备上工作得很好,只有 iOS Safari 才这样,有人在他们的 Web 应用程序中遇到过这种情况吗?

编辑 2023 05 18:

这里报告了与此行为相关的 Webkit 错误:https://bugs.webkit.org/show_bug.cgi ?id=247943

临时解决方法是处理 window.onoffline 事件以警告用户等。但是,希望他们能尽快解决这个问题。

shu*_*hen 3

我发现一个早期问题https://bugs.chromium.org/p/chromium/issues/detail?id=197841。点关闭事件会延迟触发,延迟时间取决于操作系统、浏览器等。在我的环境中,关闭事件在关闭 wifi 后 12 分钟被触发。我尝试了两种方法来修复它:

  1. 添加心跳,参考https://github.com/websockets/ws/issues/1158#issuecomment-311321579

  2. use window.addEventListener('offline',callback), 在断开连接时在回调中执行一些操作