Nodejs 关于socket.io 问题

Nodejs 关于socket.io 问题

我在做一个聊天室,需要统计在线人数 方法1:io.sockets.server.eio.clientsCount 方法2:在用户连接上的时候 定义一个 var account ,连接上就 account++ ,断开就account–

这个两个方法都有一个同样的问题 就是当我客户端 ,不断快速刷新的时候,用户的数量会不断的递增。 socket.on(‘disconnect’, function () {}) 这个监听不会触发。

求解答,这种情况的原因,以及解决方法…

3 回复

Node.js关于socket.io问题

在使用Socket.IO构建实时应用时,统计在线人数是一个常见的需求。你提到的方法虽然可以实现基本的计数功能,但在处理客户端快速刷新或异常断开连接的情况下可能会出现问题。下面我将详细解释这个问题,并提供一种更可靠的解决方案。

问题分析

当你使用 io.sockets.server.eio.clientsCount 或者在用户连接时增加 account 变量,然后在断开时减少它时,可能会遇到以下问题:

  • 快速刷新:当客户端快速刷新页面时,新的连接请求会立即发送,而旧的连接可能还没有完全断开。这会导致 account 变量不断增加。
  • 异常断开:有些情况下(如网络错误或浏览器崩溃),客户端可能无法正确地向服务器发送断开连接的通知。这会导致 socket.on('disconnect', function () {}) 监听器没有被触发。

解决方案

为了解决上述问题,我们可以采用一种更健壮的方式来管理在线用户数。具体来说,我们可以使用一个对象来记录每个客户端的连接状态,并定期检查这些状态以清理不再活跃的连接。

示例代码
const io = require('socket.io')(server);

let onlineUsers = {};

io.on('connection', (socket) => {
    console.log('A user connected');

    // 记录用户连接
    socket.on('login', (username) => {
        onlineUsers[socket.id] = { username, lastActive: Date.now() };
        io.emit('onlineUsers', Object.keys(onlineUsers));
    });

    // 更新用户活动时间
    setInterval(() => {
        onlineUsers[socket.id].lastActive = Date.now();
    }, 10000);  // 每10秒更新一次

    // 处理断开连接
    socket.on('disconnect', () => {
        delete onlineUsers[socket.id];
        io.emit('onlineUsers', Object.keys(onlineUsers));
        console.log('User disconnected');
    });
});

// 清理不活跃的连接
setInterval(() => {
    const now = Date.now();
    for (let id in onlineUsers) {
        if ((now - onlineUsers[id].lastActive) > 30000) {  // 超过30秒未活动
            delete onlineUsers[id];
        }
    }
    io.emit('onlineUsers', Object.keys(onlineUsers));
}, 30000);  // 每30秒检查一次

解释

  • 登录事件:当用户登录时,我们记录他们的连接信息,并发出当前在线用户的列表。
  • 定期更新活动状态:每10秒更新一次用户的活动时间,确保即使客户端没有主动发送心跳,我们也能知道他们是否还在线。
  • 断开连接:当用户断开连接时,删除对应的记录并更新在线用户列表。
  • 清理不活跃连接:每30秒检查一次,如果某个用户的活动时间超过30秒未更新,则认为他们是离线状态,并从在线用户列表中移除。

通过这种方式,我们可以更可靠地统计在线人数,并且能够更好地处理客户端快速刷新或异常断开的情况。


等待一段时间后,disconnect 还不会触发吗?

你可以测试一下 socket.io 带有的那些事件,看看在你不断刷新时,除了 connect 事件,还有没有什么负责断开连接的事件抛出。

原因分析

当你快速刷新页面时,浏览器会迅速打开多个连接,但这些连接可能还没来得及触发 disconnect 事件就被新的连接覆盖了。因此,account 变量会不断增加,而不是减少。

解决方案

为了解决这个问题,可以使用 socket.io 提供的 volatile 发送模式或者设置 reconnection 选项为 false 来禁用自动重连。不过,更常用的是利用 rooms 或者维护一个在线用户列表来管理连接状态。

示例代码

这里提供一个使用 rooms 的简单示例:

const io = require('socket.io')(server);

let onlineUsers = {};

io.on('connection', (socket) => {
    console.log('a user connected');

    // 将用户加入到某个房间
    socket.join(socket.id);
    
    // 更新在线用户列表
    onlineUsers[socket.id] = socket.id;
    console.log(`当前在线人数: ${Object.keys(onlineUsers).length}`);

    socket.on('disconnect', () => {
        console.log('user disconnected');
        
        // 从在线用户列表中移除该用户
        delete onlineUsers[socket.id];
        console.log(`当前在线人数: ${Object.keys(onlineUsers).length}`);
    });
});

在这个示例中,每个连接都会被分配到一个唯一的房间(以 socket.id 命名)。当用户断开连接时,对应的房间会被删除,从而更新在线用户列表。

这种方法避免了频繁刷新导致的重复连接问题,并且能准确地统计在线人数。

回到顶部