You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js WebSocket聊天应用结合MongoDB实现用户活跃状态管控咨询

实现方案

核心逻辑思路

  • WS连接建立前的握手阶段完成身份鉴权,拿到对应用户ID,实现连接与用户身份的绑定
  • 鉴权通过后先查询用户活跃状态,已处于active状态直接拒绝连接
  • 连接建立成功更新用户状态为active,内存中维护用户ID与WS连接实例的映射
  • 监听连接关闭/错误事件,触发时更新用户状态为非活跃

代码实现示例

1. 基础初始化与升级拦截

// 依赖引入,替换为你自己的实际依赖路径
const WebSocket = require('ws');
const express = require('express');
const http = require('http');
const { User } = require('./models'); // 你的用户数据库模型文件

const app = express();
const server = http.createServer(app);
// 关闭WS默认自动升级逻辑,手动处理升级请求做鉴权
const wss = new WebSocket.Server({ noServer: true });

// 内存映射表:存储用户ID与对应WS连接实例,断开连接时快速定位对应用户
const userWsMap = new Map();

// 服务启动时全局重置所有用户活跃状态,避免服务异常重启后状态残留导致用户无法登录
async function initGlobalUserStatus() {
  await User.updateMany({}, { activeStatus: 'inactive' });
}
initGlobalUserStatus();

2. 握手阶段鉴权与状态校验

server.on('upgrade', async (request, socket, head) => {
  // 替换为你现有登录体系的token校验逻辑,从Cookie取session、从query参数取token都可以
  const reqUrl = new URL(request.url, `http://${request.headers.host}`);
  const token = reqUrl.searchParams.get('token');
  if (!token) {
    socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
    socket.destroy();
    return;
  }

  // 校验token合法性,返回对应用户ID,替换为你自己的token解析逻辑
  const userId = verifyYourToken(token);
  if (!userId) {
    socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
    socket.destroy();
    return;
  }

  // 查询用户当前活跃状态,已活跃直接拒绝连接
  const targetUser = await User.findById(userId);
  if (targetUser.activeStatus === 'active') {
    socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
    socket.destroy();
    return;
  }

  // 所有校验通过,升级为WS连接
  wss.handleUpgrade(request, socket, head, (ws) => {
    wss.emit('connection', ws, request, userId);
  });
});

3. 连接建立与断开的状态更新

wss.on('connection', async (ws, request, userId) => {
  // 更新用户状态为活跃
  await User.findByIdAndUpdate(userId, { activeStatus: 'active' });
  // 给当前WS实例挂载用户ID,后续断开事件直接读取
  ws.userId = userId;
  userWsMap.set(userId, ws);

  // 监听连接关闭事件
  ws.on('close', async () => {
    await User.findByIdAndUpdate(userId, { activeStatus: 'inactive' });
    userWsMap.delete(userId);
  });

  // 监听连接异常事件
  ws.on('error', async () => {
    await User.findByIdAndUpdate(userId, { activeStatus: 'inactive' });
    userWsMap.delete(userId);
  });

  // 此处保留你原有WS消息处理逻辑
});

server.listen(3000, () => {
  console.log('服务启动在3000端口');
});

可选优化点

  • 增加心跳检测:定时给客户端发送ping包,超时未收到pong回包主动断开连接,避免网络异常断开未触发close事件导致状态异常
  • 集群部署适配:如果是多实例部署,内存Map替换为Redis存储用户连接映射,活跃状态也可以存在Redis中降低数据库查询压力
  • 主动踢人逻辑扩展:如果需要支持新登录挤掉旧登录,校验到用户已活跃时可以主动断开旧连接,再允许新连接建立

内容的提问来源于stack exchange,提问作者flaneurx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 16:45:02