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

Node.js中如何在多模块间共享Socket.IO?附项目结构示例

在Node.js其他模块中共享Socket.IO实例的方法

根据你的项目结构,这里有几种可靠的方案来实现Socket.IO实例的跨模块共享,下面结合你的现有代码一步步说明:

方案1:修改realtime.js返回IO实例,在app.js中管理并传递

首先调整你的controllers/realtime.js,让它创建Socket.IO实例后返回给调用者:

"use strict";
const config = require("../config");
const { Server } = require("socket.io"); // 显式导入Socket.IO的Server类

module.exports = (server, sessionMiddleware) => {
  // 创建Socket.IO实例并绑定到HTTP服务器
  const io = new Server(server);

  // 挂载session中间件到Socket.IO(保持你的原有逻辑)
  io.engine.use(sessionMiddleware);

  // 这里可以添加你的Socket.IO连接逻辑,比如监听connection事件
  io.on('connection', (socket) => {
    console.log(`用户已连接: ${socket.id}`);
    // 其他事件处理逻辑...
  });

  // 关键:返回创建好的IO实例
  return io;
};

接着在app.js中接收这个实例,你可以选择把它挂载到Express的app对象上,或者直接导出:

'use strict'
const express = require('express');
const session = require('express-session'); // 补充session依赖(假设你之前省略了)
const realtime = require('./controllers/realtime');

// 你的session配置(补充完整)
const sessionConfig = {
  secret: 'your-secure-secret-key',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: false } // 根据环境调整,生产环境建议设为true
};

const app = express();
const server = require('http').Server(app); 
const sessionMiddleware = session(sessionConfig);

// 初始化Socket.IO并保存实例
const io = realtime(server, sessionMiddleware);

// 方法A:把IO实例挂载到app对象上,方便在路由中通过req.app获取
app.set('io', io);

// 方法B:直接导出IO实例,供其他独立模块导入
module.exports.io = io;

// 你的其他Express配置
app.use(sessionMiddleware);
// ... 比如路由、静态文件服务等

// 启动服务器
server.listen(3000, () => {
  console.log('服务器运行在 http://localhost:3000');
});

在其他模块中使用

  • 在路由模块中(比如routes/notify.js):
'use strict';
const express = require('express');
const router = express.Router();

router.post('/send', (req, res) => {
  // 从app对象中获取IO实例
  const io = req.app.get('io');
  // 向所有客户端发送消息
  io.emit('system-notify', req.body.message);
  
  res.status(200).json({ success: true, msg: '通知已发送' });
});

module.exports = router;
  • 在独立工具/服务模块中(比如services/notification.js):
'use strict';
// 直接从app.js导入IO实例(注意避免循环引用:如果该模块被app.js导入会出问题,此时优先用app.set的方式)
const { io } = require('../app');

exports.sendGlobalAlert = (alertMsg) => {
  io.emit('global-alert', {
    type: 'warning',
    message: alertMsg
  });
};

方案2:创建独立的Socket.IO管理模块(推荐,避免循环引用)

如果你的项目比较复杂,推荐创建一个单独的模块来管理Socket.IO实例,这样更清晰且能避免循环引用问题:

创建utils/socket.js:

'use strict';
let ioInstance = null;

module.exports = {
  // 初始化Socket.IO实例
  init: (server, sessionMiddleware) => {
    const { Server } = require('socket.io');
    ioInstance = new Server(server);
    
    // 挂载session中间件
    ioInstance.engine.use(sessionMiddleware);
    
    // 初始化连接逻辑
    ioInstance.on('connection', (socket) => {
      console.log(`用户连接: ${socket.id}`);
      // 其他连接相关逻辑...
    });
    
    return ioInstance;
  },
  
  // 获取已初始化的IO实例
  getIO: () => {
    if (!ioInstance) {
      throw new Error('Socket.IO实例未初始化,请先调用init方法');
    }
    return ioInstance;
  }
};

然后修改app.js来初始化:

'use strict'
const express = require('express');
const session = require('express-session');
const socketManager = require('./utils/socket');

const sessionConfig = { /* 你的session配置 */ };
const app = express();
const server = require('http').Server(app); 
const sessionMiddleware = session(sessionConfig);

// 初始化Socket.IO
socketManager.init(server, sessionMiddleware);

// 其他Express配置...
server.listen(3000);

在任意模块中使用时:

'use strict';
const socketManager = require('../utils/socket');

function sendPrivateMessage(userId, message) {
  const io = socketManager.getIO();
  // 假设你用userId关联了socket.id,这里只是示例
  io.to(`user:${userId}`).emit('private-msg', message);
}

注意事项

  • 避免循环引用:如果模块A导入了app.js,而app.js又导入了模块A,就会出现循环引用问题,此时优先使用app.set('io', io)或者独立管理模块的方案。
  • 确保Socket.IO实例只初始化一次:无论用哪种方案,都要保证new Server(server)只执行一次,否则会创建多个Socket.IO实例导致异常。

内容的提问来源于stack exchange,提问作者Thaylor Mosquera Castro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:07:49