Socket.io为所有客户端分配相同的id

时间:2013-10-09 13:18:39

标签: javascript node.js socket.io

我正在尝试保存对象中客户端中的所有连接。但正如我所知,连接的客户端都具有相同的ID。因此,如果对象connections具有所有已连接的套接字,当我尝试向connections['mac']发送消息时,它就会显示在我发送给我的屏幕上。这是授权码:

app.post('/auth', function(req, res){   // routes.auth(hash, db, io, pseudoArray, connections)
    var username = req.body.username,
        password = req.body.password;
    if (username != "" && password != ""){
        authenticate(username, password, db, hash, function(err, user){
            if (user) {
                // Regenerate session when signing in
                // to prevent fixation 
                console.log('user authenticated');
                req.session.regenerate(function(){
                    req.session.user = user.name;
                    req.session.success = 'Authenticated as ' + user.name
                    + ' click to <a href="/logout">logout</a>. '
                    + ' You may now access <a href="/restricted">/restricted</a>.';

                    io.sockets.on('connection', function (socket) {
                        socket.set('pseudo', req.session.user, function(){
                            pseudoArray.push(req.session.user);
                            var sessionuser = req.session.user;
                            socket.emit('pseudoStatus', 'ok');
                            connections[req.session.user] = socket;
                            console.log("user " + req.session.user + " connected");
                        });
                    });
                    res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true });
                    res.redirect('home');
                });
            } else {
                console.log('auth failed');
                req.session.error = 'Authentication failed, please check your '
                    + ' username and password.'
                    + ' (use "tj" and "foobar")';
                res.redirect('login');
            }
        }); 
    } else {
        res.redirect('connect');
    }
});

我将邮件发送给其他客户的代码:

exports.addfriend = function(connections, io){
    return function(req, res){
        // var collection = db.get('go1');
        var username = req.session.user;
        var friend = req.body.name;
        console.log(connections[''+friend+'']);
        connections[''+friend+''].emit('addRequest', username);
        return;
    }
}

我之前在路由文件中有这个auth代码,我想我应该在app.js上运行它。我做到了,它仍然无法正常工作。有人能告诉我我错过了什么吗?

2 个答案:

答案 0 :(得分:4)

您的代码存在的问题是您的一条HTTP路由中有一个Socket.IO传入连接处理程序。这意味着只有在访问路由之后才应用处理程序,除了你应该只有一个Socket.IO连接处理程序。

您需要做的是分离HTTP和Socket.IO处理程序,并且由于您正在使用会话,因此允许Socket.IO处理授权。

首先,将您的套接字处理程序移到HTTP处理程序之外:

app.post('/', handler);
io.sockets.on('connection', handler);

然后,定义Socket.IO授权设置以从Express获取会话对象。假设您使用express.cookieParser(),因为您有会话和会话存储,我们需要从外部引用它们:

var MemoryStore = express.session.MemoryStore;

var session_key = 'express.sid';
var session_secret = 'for signed cookies';
var session_store = new MemoryStore();

var cookieParser = express.cookieParser(session_secret);

app.use(cookieParser);
app.use(express.session({
  secret: session_secret,
  store: session_store,
  key: session_key
});

现在可以访问会话对象和cookie解析器,我们可以定义授权设置:

io.set('authorization', function(handshake, callback) {
  if (handshake.headers.cookie) {
    cookieParser(handshake, null, function(err) {
      // Use depends on whether you have signed cookies
      // handshake.sessionID = handshake.cookies[session_key];
      handshake.sessionID = handshake.signedCookies[session_key];

      session_store.get(handshake.sessionID, function(err, session) {
        if (err || !session) {
          callback('Error or no session.', false);
        } else {
          handshake.session = session;
          callback(null, true);
        }
      });
    });
  } else {
    callback('No session cookie found.', false);
  }
});

此代码的作用是检查客户端是否有会话cookie。如果没有,它不会授权Socket.IO连接。如果是,它会解析cookie,找到关联的会话,并将会话存储在套接字中。现在您可以通过套接字访问会话属性:

app.post('/', function(req, res) {
  req.session.user = 'genericusername';
  res.send(200);
});

io.sockets.on('connection', function(socket) {
  var session = socket.handshake.session;
  session.user // genericusername
});

至于你的代码,它将如下所示:

var http = require('http');
var express = require('express');
var app = express();

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

var MemoryStore = express.session.MemoryStore;

var session_key = 'express.sid';
var session_secret = 'for signed cookies';
var session_store = new MemoryStore();

var cookieParser = express.cookieParser(session_secret);

app.use(cookieParser);
app.use(express.session({
  secret: session_secret,
  store: session_store,
  key: session_key
});

路线处理程序:

app.post('/auth', function(req, res) {
  var username = req.body.username,
  var password = req.body.password;

  if (username != '' && password != '') {
    authenticate(username, password, db, hash, function(err, user) {
      if (user) {
        req.session.regenerate(function() {
          req.session.user = user.name;
          req.session.success = 'Authenticated as ' + user.name + ' click to <a href="/logout">logout</a>. You may now access <a href="/restricted">/restricted</a>.';
          res.cookie('rememberme', '1', {
            maxAge: 900000,
            httpOnly: true
          });
          res.redirect('/home');
        });
      } else {
        req.session.error = 'Authentication failed, please check your username and password. (use "tj" and "foobar")';
        res.redirect('/login');
      }
    }); 
  } else {
    res.redirect('/connect');
  }
});

然后是Socket.IO配置:

io.set('authorization', function(handshake, callback) {
  if (handshake.headers.cookie) {
    cookieParser(handshake, null, function(err) {
      // Use depends on whether you have signed cookies
      // handshake.sessionID = handshake.cookies[session_key];
      handshake.sessionID = handshake.signedCookies[session_key];

      session_store.get(handshake.sessionID, function(err, session) {
        if (err || !session) {
          callback('Error or no session.', false);
        } else {
          handshake.session = session;
          callback(null, true);
        }
      });
    });
  } else {
    callback('No session cookie found.', false);
  }
});

io.sockets.on('connection', function(socket) {
  var session = socket.handshake.sessionl
  socket.set('pseudo', session.user, function() {
    socket.emit('pseudoStatus', 'ok');
    connections[session.user] = socket;
  });
});

答案 1 :(得分:1)

您在单个用户登录中有io.sockets.on('connection'...。因此,每次用户登录时,都会添加另一个侦听器。

所以它可能看起来像这样(某人登录,然后他们连接到套接字)

User1登录 用户1连接到套接字(伪自己)

用户2登录 用户2连接到套接字(伪作为用户1,然后伪作为用户2)

用户3登录 用户3连接到套接字(伪为1,2,然后3

如果用户1刷新页面,他将伪为1,2,3,以及

每次有人登录时,问题就越来越多。

您需要在多次发生的函数之外侦听连接。

我通常做类似以下的事情

英文:用户加载一个页面,他连接到socket io,然后他做一个ajax $ .get,这样服务器就可以使用他的会话给他一个授权他的套接字的令牌。然后他发送令牌,服务器知道套接字已被授权。

app.get('/socket_token', function(req, res, next){
  var socket_token = Math.random();/* probably want to use crypto here instead*/
  mysql.query('update users set users.socket_token = ? where users.id = ?', [  socket_token, req.session.userId], function(err, result){
    if(result)
      res.json({userId: req.session.userId, socket_token: socket_token});
    else
      res.send('DOH!');
   });
});

global.all_my_sockets = []

io.sockets.on('connnection', function(socket){

  socket.emit('hey_you_are_connected');

  socket.on('authorize', function(credentials, callback){
    if(credentials.userId)
      mysql.query('select * from users where id = ?', [credentials.userId], function(err, user){
        if(user.socket_token == credentials.socket_token){
          socket.set('authorized', true);
          socket.set('userId', user.id);

          // This is probably bad since what if he has 2 browser windows open?
          all_my_sockets[user.id] = socket

          callback('that socket token is authorized');
        } else {
         //handle it
         callback('that socket token is no good');
        }
      })

  });
  socket.on('disconnect', function(){
    socket.get('userId', function(id){
      if(id)
        delete all_my_sockets[id];
    });
  });
})

并在客户端

socket = io.connect();
socket.on('hey_you_are_connected', function(){
  $.get('/socket_token', function(credentials){
    socket.emit('authorize_token', credentials, function(res){
      console.log(res);  // did we authorize socket?
    });
  });
});

我把它排在了我的头顶,所以可能会有错误,但它应该非常接近你想要做的事情。

因此在all_my_sockets [id]中保存套接字存在一些问题,即每个用户只能有1个套接字,如果有多个浏览器打开会怎样?

您应该尝试以不同的方式构建代码,这样您就不需要全局套接字,否则,只需将它们推送到数组中,这样每个用户就可以存在多个。

all_my_sockets.push(socket);
...
socket.on('disconnect', function(){
  if( all_my_sockets.indexOf(socket) != -1 )
    all_my_sockets.splice( all_my_sockets.indexOf(socket), 1 )
})

如果您在授权之前将每个套接字推送到数组,然后实际找到套接字,则必须通过每个套接字:

_.each(all_my_sockets, function(socket){
  socket.get('userId', function(userId) { 
    if(userId)
      doSomething(socket)
    else
      doSomethingUnAuthorized(socket)
   });
 });

显然这很慢,这就是为什么没有很多人将套接字保存到这样的全球地方的原因。尝试使用更多回调来尝试做什么,并从头开始重新思考问题。或者将它留作黑客,直到你获得更多流量,这成为一个性能问题。 :)