正确关闭WebSocket(HTML5,Javascript)

143

我正在尝试使用HTML5 WebSockets。我想知道如何优雅地关闭连接?例如,如果用户刷新页面或关闭浏览器会发生什么?

如果用户在不调用websocket.close()的情况下刷新页面,会出现奇怪的行为 - 刷新后将触发websocket.onclose事件。

7个回答

129
根据当前支持浏览器实现的 协议规范 v76:

为了清除连接,一个由仅含0xFF和0x00字节的数据帧会被发送,要求另一个节点关闭连接。

如果你正在编写服务器,应确保在服务器关闭客户端连接时发送关闭数据帧。通常的TCP套接字关闭方法有时会很慢,并导致应用程序认为连接仍然打开,即使它已经关闭。
当你关闭或重新加载页面时,浏览器应该自动处理此操作。但是,你可以通过捕获beforeunload事件来确保发送关闭数据帧:
window.onbeforeunload = function() {
    websocket.onclose = function () {}; // disable onclose handler first
    websocket.close();
};

我不确定在刷新页面后如何会收到onclose事件。WebSocket对象(带有onclose处理程序)将在页面重新加载后不存在。如果您正在尝试在页面加载时立即建立WebSocket连接,则可能会遇到一个问题,即服务器在旧连接断开后不久就拒绝新连接(或者浏览器在您尝试连接的时候还没有准备好进行连接),因此您会收到新WebSocket对象的onclose事件。

2
在Firefox中可能会出现连接似乎挂在到下一个页面加载的情况。我找不到参考,但我认为可能有一个关于这个问题的bug。另一种可能性是'onclose'事件意外地触发,或者可能是有目的地触发,当用户导航/页面重新加载时。我已经发布了一个问题,询问应该有什么预期行为,哪个浏览器是正确的,以及我们如何实现自动重连。 - leggetter
5
请考虑这些问题:在 Firefox、Safari 和 Opera 浏览器中,onbeforeunload 事件无法正常工作。 - artkoenig

38
事实上,今天使用的WebSockets有两个主要协议版本。旧版使用[0x00][message][0xFF]协议,而新版使用Hybi格式数据包
Opera和iPod/iPad/iPhone使用旧版协议,因此在WebSockets服务器中实现向后兼容非常重要。这些浏览器使用旧协议时,我发现刷新页面、导航离开页面或关闭浏览器都会自动关闭连接。太好了!
但是对于使用新协议版本的浏览器(如Firefox、Chrome和IE10),只有关闭浏览器才会自动关闭连接。也就是说,如果您刷新页面或从页面导航离开,浏览器不会自动关闭连接。但是,浏览器会向服务器发送一个带有第一个字节(proto ident)为0x88(更好地称为关闭数据帧)的hybi数据包。一旦服务器接收到此数据包,它可以强制关闭连接(如果您选择这样做)。

4
在服务器端,如何管理这个HyBi数据包的实际例子? - albanx
12
看起来不关闭连接似乎很荒谬。WebSocket变量在页面重新加载时被清除,所以如果无法访问,为什么连接仍然保持开放呢?重复使用连接也没有意义。 - Triynko
@albanx 请查看我下面的答案。 - artkoenig
有没有关于如何从WebSocket客户端(即浏览器)发送关闭帧的代码示例?我正在使用ws npm模块。 - Shaik Syed Ali

36

非常简单,你只需要关闭它 :)

var myWebSocket = new WebSocket("ws://example.org"); 
myWebSocket.send("Hello Web Sockets!"); 
myWebSocket.close();

你是否也查看了这个网站并检查过Opera介绍文章


5
正如theoobe所说,一些浏览器不会自动关闭Websockets。不要尝试在客户端处理任何“关闭浏览器窗口”事件。如果您考虑到主要的桌面和移动浏览器的支持(例如,在Mobile Safari中,onbeforeunload无法工作),目前还没有可靠的方法来解决这个问题。我在服务器端处理此问题方面有很好的经验。例如,如果您使用Java EE,请查看javax.websocket.Endpoint,取决于浏览器,如果您关闭/重新加载浏览器窗口,则会调用OnClose方法或OnError方法。

1
在我的情况下,在数据传输期间连接被关闭,在Opera和iOS系列浏览器中运行良好。请帮我解决问题..自上两周以来,我一直在与这个问题斗争。https://dev59.com/jF0a5IYBdhLWcg3wCEsw - Mrug

4
请使用此内容。
var uri = "ws://localhost:5000/ws";
var socket = new WebSocket(uri);
socket.onclose = function (e){
    console.log(connection closed);
};
window.addEventListener("unload", function () {
    if(socket.readyState == WebSocket.OPEN)
        socket.close();
});

关闭浏览器不会触发 WebSocket 的关闭事件,您必须手动调用 socket.close()

看起来,正如@artkoenig所说,unload事件并不可靠。 - undefined

1
通过使用 Web Socket 的 close 方法,您可以根据需要编写任何函数。
var connection = new WebSocket('ws://127.0.0.1:1337');
    connection.onclose = () => {
            console.log('Web Socket Connection Closed');
        };

1

beforeunload事件应该避免使用。来自MDN

特别是在移动设备上,beforeunload事件不可靠。

此外,它只应用于保存用户数据,而不是关闭websocket连接。

与其在beforeunload上关闭连接,我会使用pagehide或(备选)unload事件,正如Chrome开发者文档所建议的那样。

const terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload';
window.addEventListener(terminationEvent, (event) => {
    if (event.persisted === false) {
        // client is gone
        socket.onclose = function () { };
        socket.close();
    }
});

除了被规范推荐之外,这种方法的优点是即使用户选择留在页面上,您仍然可以有一个提示确认用户是否要离开 (window.addEventListener("beforeunload", () => return "Are you sure?");),而不会断开用户的连接。
正如 Chrome 文档所说 (重点添加):

此时文档仍然可见,事件仍然可取消

如果用户实际上没有离开页面,这可能导致意外关闭 WebSocket 连接。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接