3

我很难将所有这三个放在一起,可能是因为我没有正确理解 Express 路由的概念。

我有一个带有事件更新的 RabbitMQ 队列。我们可以通过它们的 id 来识别这些事件。所以我想在一个给定的页面上找到一个事件,只是对应于它的 id 的更新。

队列:1316, 1539, 3486, 3479, 1316, 3890, 3479, ... -> 无限期地从数据库馈送。www.example.com/event/1316 -> 从队列中获取 ID 为 1316 的消息 www.example.com/event/3479 -> 从队列中获取 ID 为 3479 的消息

当我加载第一个事件时,我的代码运行良好,但是当我在不同的窗口中加载第二个事件时,它会从两个事件中获取消息,如果我加载第三个事件,猜对了,它会从三个 id 中获取消息。

应用程序.js

var express = require('express')  
, http = require('http');
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server, { log: false });
require('./io')(io);

var amqp = require('amqp');
var rabbitMQ = amqp.createConnection({ host: 'localhost' });

rabbitMQ.on('ready', function() {
  console.log('Connected to RabbitMQ');
  io.sockets.on('connection', function (socket) {
    console.log('Socket connected: ' + socket.id);
    rabbitMQ.queue('offer', { autoDelete: false, durable: false, exclusive: false }, function(q) {    
      q.bind('#'); // Catch all messages    
      q.subscribe(function (message) {
        obj = JSON.parse(message.data.toString());
        //socket.broadcast.to(obj.id).emit('message', obj);
        io.sockets.in(obj.id).emit('message', obj);
      });
    });
  });
});

var routes = require('./routes')
, event = require('./routes/event');

app.get('/', routes.index);
app.get('/event/:id', event.index);

server.listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});

io.js

var socketio = function (io) { 
  if (!io) return socketio._io;  
  socketio._io = io;
} 

module.exports = socketio;

路线/事件.js

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

exports.index = function(req, res) {
  io.sockets.on('connection', function (socket) {
    socket.join(req.params.id);
  });
  res.render('event', { title: 'Event' });
};

谢谢!

4

2 回答 2

2

你收到它们都是因为你join,而不是leave房间。如果您从 wiki 中查看Socket IO Rooms,在底部,它提供io.sockets.manager.roomClients[socket.id]了一种获取套接字已加入的房间列表的方法(如果您访问了所有三个链接,我怀疑这将包括所有三个)。

您可能想尝试浏览此房间列表以及leave任何不是当前房间的房间,看看是否能解决问题。

编辑

好的,所以,有两个原因/解决方案。我刚刚测试了我的理论,它是有效的——你会收到你join编辑过的每个房间的消息,并且会继续这样做,直到你收到leave它们为止。所以这里是选项:

1.leave所有其他房间当他们join一个房间

io.sockets.on('connection', function (socket) {
    var room = req.params.id;

    var roomKeys = Object.keys(io.sockets.manager.roomClients[socket.id]);
    roomKeys.forEach(function(key) {
        if (key === '' || key === '/' + room) return;
        socket.leave(key.slice(1));
    });

    socket.join(room);
});

如前所述,我对此进行了测试。有用。

2.不发送message事件,发送{room name}事件

您可以发出 '{room name}' 事件,而不是发出 'message' 事件。而不是您的q.subscribe()回调包含io.sockets.in(obj.id).emit('message', obj);,您只需这样做socket.emit(obj.id, obj);,并且您将让 javascript 客户端仅侦听该页面的事件类型(基于 URL 路径)。

我也测试了这个。它也有效。它也更简单(我认为)因为它只需要.emit()在你的q.subscribe()回调中,这意味着你保存了“房间管理”的东西。

于 2012-10-25T17:31:06.233 回答
2

在尝试和失败之后,我明白我做错了什么,因为io.sockets.on('connection')在路由器内部使用是复制事件。所以归根结底,最简单的思维方式是正确的。

应用程序.js

var room = '';
var roomHandler = function(req, res, next) {
  if (req.path.match('event')) {
    room = req.params.id;
  } 
  next(); // Passing the request to the next handler in the stack.
}

io.sockets.on('connection', function (socket) {    
  socket.join(room);
});

rabbitMQ.on('ready', function() { 
  rabbitMQ.queue('offer', { autoDelete: false, durable: false, exclusive: false }, function(q) {      
    q.bind('#'); // Catch all messages  
    q.subscribe(function (message) {
      obj = JSON.parse(message.data.toString());
      io.sockets.in(obj.id).emit('message', obj);
    });
  });
});

app.get('/', routes.index);
app.get('/event/:id', roomHandler, event.index);
于 2012-11-06T08:45:30.537 回答