如何在 Express 4 路由中使用 socket.io 向连接的套接字发出事件?

Posted

技术标签:

【中文标题】如何在 Express 4 路由中使用 socket.io 向连接的套接字发出事件?【英文标题】:How can I emit events to connected sockets using socket.io from within my Express 4 routes? 【发布时间】:2015-04-01 23:03:18 【问题描述】:

这是其他人提出的一个问题,但由于我的 Express 设置不同,我无法从他们给出的答案中受益。

我已经在我的服务器上实现了 socket.io 并以一种简单的方式工作。它是这样工作的:

bin/www:

#!/usr/bin/env node
var debug = require('debug')('gokibitz');
var app = require('../../server');

app.set('port', process.env.PORT || 3000);

var server = app.listen(app.get('port'), function() 
    debug('Express server listening on port ' + server.address().port);
);

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

io.on('connection', require('../routes/socket.js'));

我所有的路线和其他 Express 设置都在 ../../server.js

var routes = require('./server/routes/index');
var user = require('./server/routes/user');
...

app.use('/', routes);
app.use('/api/user/', user);
...
app.use('*', routes);

然后在../routes/socket.js,我有这个样板:

module.exports = function (socket) 
    socket.emit('send:news',  hello: 'world' );

    setInterval(function () 
        socket.emit('send:time', 
            time: (new Date()).toString()
        );
    , 1000);

    return socket;
;

我可以补充一下,这很好用。但现在我希望能够在我非常普通的 Express 应用程序中从各种路由发出事件,但我终其一生都无法找出正确的方法来获取对我需要的套接字对象的引用。

示例:当用户发表评论时,我想向所有连接的用户发出一个事件,通知他们有新评论。从我的路由文件 (./server/routes/user.js) 中,我怎样才能访问我需要发出事件的对象?

以下是路由文件中相关位的骨架:

var express = require('express');
var router = express.Router();

router.post('/', function (req, res) 
  ...
);

module.exports = router;

我唯一可以访问的地方是../routes/socket.js文件,没用。

在传入任何 io 或 socket 对象之前,所有路由都设置在 app.js 中。

我应该能够require('socket.io') 并以某种方式使用它来向所有连接的套接字发射吗?

是否有一种明智的方式将连接的套接字存储在 ../routes/socket.js 上,以便可以从其他路由中获取和发出?

谁能引导我朝着正确的方向前进?

【问题讨论】:

从路由中,您是否尝试向所有连接的用户广播消息?还是只发送给在路由上提出请求的用户?你想从你的路线发送给谁? io.emit(msg, data) 将发送给所有连接的客户端。如果您从另一个模块执行此操作,则必须将 io 变量共享给该其他模块。 我正在尝试从一条路由向所有连接的用户广播一条消息。示例:用户向帖子添加评论。我想向所有用户广播(命名空间)事件,以便查看该帖子的任何人都可以收听更新并使用新评论刷新他们的页面。我知道我需要共享 io 对象,但我正在努力寻找正确的方法来使用我的设置。 在最后一种情况下,您需要创建一个单例返回对象或使其全局化。 【参考方案1】:

我最终能够使用以下示例使事情正常进行: https://github.com/expressjs/generator/issues/45#issuecomment-53719435

我创建了一个名为server/io.js的新文件:

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

io.on('connection', function (socket) 
  socket.emit('news',  hello: 'world' );
  socket.on('my other event', function (data) 
    console.log(data);
  );
);

module.exports = io;

然后我将server/bin/www 更新为:

#!/usr/bin/env node
var debug = require('debug')('gokibitz');
var app = require('../../server');
var io = require('../io');

app.set('port', process.env.PORT || 3000);

var server = app.listen(app.get('port'), function() 
    debug('Express server listening on port ' + server.address().port);
);

io.attach(server);

然后,在我的路线中,我使用这个:

var io = require('../io');
...
io.emit(...);

至少对我来说,缺少的部分是能够单独创建io 对象,返回正确的对象,然后在bin/www 中使用io.attach(server) 在正确的位置启动它。

希望这将帮助其他人追随我的脚步。

【讨论】:

【参考方案2】:

我认为您对这些概念感到困惑。 socket.io 库使用或模拟与客户端的 websocket(双向套接字),与路由无关。

您可以使用 io 对象向所有套接字发送通知:

io.emit('message_to_all', true);

你必须在 io.sockets 上有一个数组,包含所有的套接字。

您可以使用命名空间或房间,我建议您学习文档:

http://socket.io/docs/rooms-and-namespaces/#

添加一些东西:

如果您想向同一路线中的​​所有人发送消息,您可以加入同名路径的频道。

例如在客户端:

var socket = io(window.location.href); // Or route..

还有服务器:

var nsp = io.of('/the/specific/route');
nsp.on('connection', function(socket)
  // ...
);
nsp.emit('message_to_all_in_route', data);

关于上一题编辑:

您可以使用 Express 中间件 API 将请求或响应对象中的 io 对象发送到路由:

例如:

#!/usr/bin/env node
var debug = require('debug')('gokibitz');
var app = require('../../server');

app.set('port', process.env.PORT || 3000);

var server = app.listen(app.get('port'), function() 
    debug('Express server listening on port ' + server.address().port);
);

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

app.use(function (req, res, next) 
  res.io = io;
  next();
);

io.on('connection', require('../routes/socket.js'));

在路线中:

route.post('/post/:id/comments', function (req, res) 

   // Do your logic

  var postio = res.io.of('/post/' + req.params.id);
  postio.emit('new_comment', commentData);

);

【讨论】:

使用io对象似乎很简单,但我不知道如何正确共享io对象,以便我在单独文件中的路由可以使用它。 使用命名空间(如我在回复中添加的示例),您可以使用 io.of 方法定义此路由。 我认为我们正在谈论彼此。我更新了我的问题,以阐明我如何努力访问我需要发出事件的对象。 您关于使用中间件的编辑看起来正是我所需要的:让我确保我可以让它工作。 当中间件被添加到app 时似乎没有被调用:你确定它应该在路由之后和app.listen 之后添加时工作吗?也许我错过了什么。【参考方案3】:

正如我在评论中所说,您可以发送给所有连接的客户:

io.emit(msg, data);

这将需要访问您在第一个模块中创建的io 对象。与您的模块与您的路由模块共享它的常用方法是从路由模块导出一个方法,该方法允许您将io 对象传递给它,然后在您需要routes 模块并创建@ 987654325@ 对象,你只需调用该方法将io 对象传递给路由模块,它可以将其保存在本地以备将来使用。

io 只是一个共享对象,就像app 对象一样。如果您希望一个模块能够使用共享对象,通常的方法是您调用该模块中的某个方法与它共享一些它可以使用的对象。

【讨论】:

恐怕你高估了我从你的一般建议中受益的能力。我用我的一个路由文件的结构更新了我的问题:如何修改它以访问bin/www 中的io 对象?我尝试将module.export.io = io; 添加到bin/wwwvar io = require('../bin/www').io 到我的路线,但io 只是未定义。【参考方案4】:

我在该文件中创建了一个新文件sockets.js

var socketio = require('socket.io');
var io;

module.exports = 
socketServer: function (app) 
    io = socketio.listen(app);

    io.on('connection', function (socket) 

        console.log(Object.keys(users));
        socket.on('message', function (data) 
            io.emit('updateChat', 'Hello World!')
        );
    );
  

在我的app.js:

var express = require('express');
var app = express();
var http = require('http').createServer(app);
var port = process.env.PORT || 3000;

var io = require('./sockets').socketServer(http);
http.listen(port, function () 
    console.log('SERVER RUNNING.. PORT: ' + port)
);

这对我有用。祝你好运!

【讨论】:

以上是关于如何在 Express 4 路由中使用 socket.io 向连接的套接字发出事件?的主要内容,如果未能解决你的问题,请参考以下文章

路由 + Express 路由

路由 + Express 路由

Express 4.15.5 不渲染 Angular 7 路由

如何使用 Busboy 访问 Express 4 req.body 对象

如何使用 express 中间件处理所有路由?

Angular 和 Express 路由如何在 mean.js 应用程序中协同工作?