如何处理 Socket.io 中的 Close 事件?

14

我正在制作一个基于Web的简单在线游戏。该游戏使用Socket.io进行网络连接。但我遇到了问题。

考虑以下情况。我运行了Socket.io服务器。一个玩家创建了房间,另一个玩家加入了房间。他们玩了一段时间的游戏...但是其中一个玩家非常生气,关闭了游戏选项卡。

在这种情况下,我如何在服务器端获取客户端已关闭浏览器的事件?

根据我的谷歌搜索,有人说:“使用像onBeforeUnload这样的浏览器关闭事件”

但我知道,并不是所有的浏览器都支持onBeforeUnload事件。因此,我想要在服务器端检查客户端断开连接事件的解决方案。

在Socket.io(nodeJS)服务器端控制台上,当客户端的连接关闭时,控制台会显示如下信息:

debug - discarding transport

我的nodeJS版本是0.4.10,Socket.io版本是0.8.7,两者都在Linux上运行。

请问有人能够提供帮助吗?

以下是简化的代码:

var io = require ( "socket.io" ).listen ( 3335 );
io.sockets.on ( "connection" , function ( socket )
{
  socket.on ( "req_create_room" , function ( roomId ) 
  {
    var socketInstance = io
    .of ( "/" + roomId )
    .on ( "connection" , function ( sock )
    {
       sock.on ( "disconnect" , function () 
       {
          // i want this socket data always displayed...
          // but first-connected-client doesn't fire this event ..
          console.log ( sock );
       }
    });
  });
});

我的回答有帮助吗?解决了你的问题吗? - Carlos Atencio
2个回答

20

更新:我为这个解决方案创建了一篇博客文章。欢迎提供任何反馈!

我建议在Socket IO中使用“同步卸载时断开连接”选项。我曾经遇到过类似的问题,这个选项真的帮助了我。

在客户端上:

var socket = io.connect(<your_url>, {
'sync disconnect on unload': true });

不需要在任何unloadbeforeunload事件中进行连线。我在几个浏览器中尝试过这个方法,到目前为止它完美地发挥作用。


2
您真是让我开心啊!我浪费了七个小时来解决这个问题! - Joel Murphy
请将链接链接到帖子,而不是博客。 - laggingreflex
这还能用吗?我目前正在尝试将其添加到现有系统中,但我不确定客户端是否没有发送断开连接请求或服务器是否忽略了它。 - Florian Wendelborn
现在对我来说仍然有效! - isaacsan 123

9

当一个 socket.io 连接断开时,会触发一个名为 disconnect 的事件(请注意,你需要这个事件,因为你可能仍然有一个网页打开,但是如果你的互联网连接断开了怎么办?)。尝试一下:

var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  socket.on('disconnect', function () {
    io.sockets.emit('user disconnected');
  });
});

在您的服务器上。摘自Socket.IO网站

//编辑

我看了一下您的代码,在我的地方进行了一些测试。我得到了与您完全相同的结果,以下是我的解释。您试图通过动态强制使Socket.IO监听不同的url(这些url在运行时添加)来使其非常动态。但是,当第一个连接建立时,服务器此时没有监听其他url。似乎正是在这个时刻(当接受连接时),为第一个连接设置了disconnect处理程序,并且稍后没有更新(请注意,当您在客户端调用io.connect多次时,Socket.IO不会创建新的连接)。总的来说,这似乎是一个错误!或者也许有一些花哨的解释可以解释为什么应该是这样,但我不知道。

让我告诉您一些其他的事情。首先,这种动态创建侦听器的方法似乎不是一个好方法。在我看来,您应该执行以下操作:存储现有的房间,并使用一个url来处理它们。保留房间的ID,当您例如从客户端发出message事件时,将房间的ID添加到数据中,并使用一个message处理程序来处理此数据。我想您已经明白了。将动态部分推入数据而不是url。但我可能是错的,至少这是我的看法。

另一件事是,您编写的代码似乎很糟糕。请注意,多次运行.on('connection',handler)会使其多次触发。处理程序一个接一个地堆叠起来,它们不互相替换。因此,这就是我实现此操作的方式:

var io = require("socket.io").listen(app);
var roomIds = [];

function update_listeners(id) {
    io.of("/"+id).on("connection", function(socket) {
        console.log("I'm in room " + id);
        socket.on("disconnect", function(s) {
           console.log("Disconnected from " + roomId);
        });            
    });
}

var test = io.sockets.on("connection", function(socket) {
    console.log("I'm in global connection handler");
    socket.on("req_create_room", function(data) {
        if (roomIds.indexOf(data.roomId) == -1 ) {
            roomIds.push(data.roomId);
            update_listeners(data.roomId);    
        }
        test.emit("room_created", {ok:true}); 
    });
    socket.on("disconnect", function(s) {
        console.log("Disconnected from global handler");
    });
});

请记住,在定义监听器之前创建连接的问题仍将发生。

谢谢!它可以工作。但我仍有一些问题 ... 在“断开连接”事件正确触发,但除了第一个连接的客户端以外。我想知道为什么第一个连接的客户端无法触发“断开连接”事件? - Jindong Jung
我发布了代码。那是简化版本..谢谢你的帮助。 - Jindong Jung
@JindongJung 我已经更新了我的回答,希望能对你有所帮助。如果我所写的是真实的,即这是一个 bug,那么我认为你应该向 Socket.IO 的开发人员反馈。希望你能够这样做。祝你好运。 - freakish
我已经找到解决方案了。全局套接字通常会触发,所以我将使用全局套接字来检测客户端浏览器的断开连接。谢谢你的帮助,对我非常有帮助 :) - Jindong Jung

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