【问题标题】:webSocketServer node.js how to differentiate clientswebSocketServer node.js如何区分客户端
【发布时间】:2012-11-02 02:07:27
【问题描述】:

我正在尝试将套接字与 node.js 一起使用,我成功了,但我不知道如何在我的代码中区分客户端。 关于套接字的部分是这样的:

var WebSocketServer = require('ws').Server, 
    wss = new WebSocketServer({port: 8080});
wss.on('connection', function(ws) {
    ws.on('message', function(message) {
        console.log('received: %s', message); 
        ws.send(message);
    });
    ws.send('something');
});

此代码适用于我的客户端 js。

但我想向特定用户或所有在我的服务器上打开套接字的用户发送消息。

在我的情况下,我作为客户端发送消息并收到响应,但其他用户什么也没显示。

我想例如 user1 通过 webSocket 向服务器发送一条消息,然后我向他的套接字打开的 user2 发送一条通知。

【问题讨论】:

  • 如何定义哪个连接是“user1”,哪个是“user2”?
  • 我真的不知道,我想在连接的时候给用户一种socketSession
  • 好吧,一旦您知道连接的用户身份(例如,当您的用户将他的用户名作为消息发送时),您就可以将其命名引用存储在字典(例如connections[username] = ws),然后在其他地方,你可以做类似connections[username].send(message)
  • 如果您的需要不是完全针对单个用户,而是将一组用户(可能是一组 1)隔离到“房间”中,您可以使用套接字 join()broadcast() 方法。在这里查看一些讨论:stackoverflow.com/questions/6846174/…
  • 谢谢它可以很好地将我所有的ws注册到一个数组中:)

标签: node.js websocket


【解决方案1】:

Worlize 服务器中的This code snippet 对我帮助很大。即使您使用的是 ws,代码也应该很容易适应。我在这里选择了重要的部分:

// initialization
var connections = {};
var connectionIDCounter = 0;

// when handling a new connection
connection.id = connectionIDCounter ++;
connections[connection.id] = connection;
// in your case you would rewrite these 2 lines as
ws.id = connectionIDCounter ++;
connections[ws.id] = ws;

// when a connection is closed
delete connections[connection.id];
// in your case you would rewrite this line as
delete connections[ws.id];

现在您可以轻松地创建一个 broadcast() 和 sendToConnectionId() 函数,如链接代码所示。

希望对您有所帮助。

【讨论】:

  • 需要使用 for in 循环来广播,而不是标准的 for 循环,因为(关联)数组中有“漏洞”。
【解决方案2】:

您可以简单地将用户 ID 分配给数组 CLIENTS[],这将包含所有用户。您可以直接向所有用户发送消息,如下所示:

var WebSocketServer = require('ws').Server,
    wss = new WebSocketServer({port: 8080}),
    CLIENTS=[];

wss.on('connection', function(ws) {
    CLIENTS.push(ws);
    ws.on('message', function(message) {
        console.log('received: %s', message);
        sendAll(message);
    });
    ws.send("NEW USER JOINED");
});

function sendAll (message) {
    for (var i=0; i<CLIENTS.length; i++) {
        CLIENTS[i].send("Message: " + message);
    }
}

【讨论】:

  • 如果来回发送客户端的唯一ID,也可以获得消息的源客户端。
  • 此解决方案无法扩展,因为变量 CLIENTS 存在于 NodeJS 的单个进程中。即使我们通过分叉多个进程来利用多个内核,CLIENTS 也不是共享内存分配,每个进程都维护自己的 CLIENTS 列表。因此,它需要后续连接才能与同一进程对话。
  • @KhanhHua 能否提供一个可扩展的解决方案
  • 您可以使用 Redis pub-sub 并创建一个“发布”通道,供您拥有的每台服务器监听。每当在该频道上发布消息时,每个服务器都会向其所有客户端广播。就这么简单。
  • Per the docs, WebSocketServer 有一个 clients 属性可以为您跟踪当前客户。文档还提供了一个迭代该集合以向每个客户端发送广播消息的示例。
【解决方案3】:

我正在使用来自 ws 对象的 fd。每个客户端应该是唯一的。

var clientID = ws._socket._handle.fd;

当我打开一个新的浏览器标签时,我得到一个不同的号码。

第一个 ws 有 11 个,下一个有 12 个。

【讨论】:

  • 我认为如果一个用户离开而另一个用户加入,文件描述符 fd 可以被重用......基本上他们可能会被回收
  • 如果使用离开,您可以将其从会话中删除,当新的加入时即使使用相同的号码,您的会话表中也会有未填充的键
  • 可能有问题,在断开连接时 _handle 将消失,您无法将其从表中移除,也许您可​​以将句柄保存在 ws 中?
【解决方案4】:

这取决于您使用的是哪个 websocket。例如,最快的,在这里找到:https://github.com/websockets/ws 能够通过这种方法进行广播:

var WebSocketServer = require('ws').Server,
   wss = new WebSocketServer({host:'xxxx',port:xxxx}),
   users = [];
wss.broadcast = function broadcast(data) {
wss.clients.forEach(function each(client) {
  client.send(data);
 });
};

稍后在您的代码中,您可以使用 wss.broadcast(message) 发送给所有人。为了向个人用户发送 PM,我执行以下操作:

(1) 在我发送到服务器的消息中,我包含了一个用户名 (2) 然后,在 onMessage 中,我使用该用户名将 websocket 保存在数组中,然后稍后通过用户名检索它:

wss.on('connection', function(ws) {

  ws.on('message', function(message) {

      users[message.userName] = ws;

(3) 要发送给特定用户,您可以执行 users[userName].send(message);

【讨论】:

  • 能否提供包含用户名的代码链接?
  • 在客户端,当您发送消息时,您可以创建一个 JSON 对象,然后将其字符串化,如下所示: var sendObject = {userName:"Tim",pm:true,message:"你好吗? "}; ws.send(JSON.stringify(sendObject));此外,在 Einaros 中,您可以在标头中发送 json 有效负载,如下所示: WS.open(uri, [JSON.stringify({userName:"Tim"})]);然后,您可以在服务器上使用 ws.upgradeReq.headers 对其进行解析,并可以通过 JSON.parse 将字符串转回其对象。我用这种方法创建用户组,但也可以用来存储用户数组。
  • 我也有类似的问题,但没能解决,你能看看吗? stackoverflow.com/questions/35535700/…
  • @johannesMatevosyan 如果您认为我的回答是正确的,请您投票。谢谢。我评论了你的 plunk,发现一个拼写错误可能会影响事情。
  • 我一定会的,首先我需要明天检查一下。
【解决方案5】:

如果您指的是打开的连接,则客户端可以使用ws.upgradeReq.headers['sec-websocket-key'] 作为标识符。并将所有套接字对象保存在一个数组中。

但如果您想识别您的用户,则需要将用户特定数据添加到套接字对象。

【讨论】:

    【解决方案6】:

    您可以检查连接对象。它为每个连接的客户端提供内置标识;你可以在这里找到它:

    let id=ws._ultron.id;
    console.log(id);
    

    【讨论】:

    • 你怎么知道 Ultron.id 是独一无二的?我找不到与此相关的任何内容。
    • 使用不同的连接自行尝试。
    • ultron 在去年 1 月的 5.1、5.0 和 4.1 版本中作为 websockets/ws 的依赖项被删除。使用这些(或更高版本)版本时,ws._ultron 将不再存在。
    【解决方案7】:

    在nodejs中可以直接修改ws客户端,分别为每个客户端添加自定义属性。你还有一个全局变量 wss.clients 并且可以在任何地方使用。请尝试下一个代码并尝试连接至少两个客户端:

    var WebSocketServer = require('ws').Server;
    var wss = new WebSocketServer({
        server: httpsServer
    });
    
    
    wss.getUniqueID = function () {
        function s4() {
            return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
        }
        return s4() + s4() + '-' + s4();
    };
    
    wss.on('connection', function connection(ws, req) {
        ws.id = wss.getUniqueID();
    
        wss.clients.forEach(function each(client) {
            console.log('Client.ID: ' + client.id);
        });
    });
    

    也可以直接在客户端连接URL中传参数:

    https://myhost:8080?myCustomParam=1111&amp;myCustomID=2222

    在连接函数中你可以获取这些参数并将这些参数直接分配给你的ws客户端:

    wss.on('connection', function connection(ws, req) {
    
        const parameters = url.parse(req.url, true);
    
        ws.uid = wss.getUniqueID();
        ws.chatRoom = {uid: parameters.query.myCustomID};
        ws.hereMyCustomParameter = parameters.query.myCustomParam;
    }
    

    【讨论】:

    • 有没有办法在不改变ws的情况下做到这一点?在执行ws.clients.values() 时,有没有办法从该列表中获取请求?
    • +1 为答案。添加支持它的参考:github.com/websockets/ws/issues/859
    • 这太棒了,谢谢!我正在使用 websocket 库(而不是 ws)所以我必须使用 req.resourceURL 这包括一个解析的查询字符串作为对象的一部分,所以你可以只使用 req.resourceURL.query.myCustomParam
    • 在 TypeScript 中,我收到 Property 'id' does not exist on type 'WebSocket' 错误。有没有办法做到这一点,而无需像here 所述那样转换为any
    【解决方案8】:

    这里一种可能的解决方案是在用户 ID 前面附加 deviceId,这样我们就可以将具有相同用户 ID 但在不同设备上的多个用户分开。

    ws://xxxxxxx:9000/userID/&lt;&lt;deviceId&gt;&gt;

    【讨论】:

      【解决方案9】:

      你可以使用请求头'sec-websocket-key'

      wss.on('connection', (ws, req) => {
        ws.id = req.headers['sec-websocket-key']; 
      
        //statements...
      });
      

      【讨论】:

      • sec-websocket-key 冲突怎么样?
      【解决方案10】:

      如果这里有人可能正在使用koa-websocket 库,WebSocket 的服务器实例将与请求一起附加到ctx。这使得操作wss.clients 集(ws 中的会话集)变得非常容易。例如通过 URL 传递参数并将其添加到 Websocket 实例,如下所示:

      const wss = ctx.app.ws.server
      const { userId } = ctx.request.query
      
      try{
      
         ctx.websocket.uid = userId
      
      }catch(err){
          console.log(err)
      }
      

      【讨论】:

        【解决方案11】:

        使用全局计数器变量并为每个新连接分配其值:

        const wss = new WebSocket.Server({server});
        let count_clients = 0;
        wss.on('connection', function connection(ws){
            ws.id=count_clients++;
            console.log(`new connection, ws.id=${ws.id}, ${ws._socket.remoteAddress}:${ws._socket.remotePort} #clients=${wss.clients.size}`);
            ws.on('close', req => {console.log(`disconnected, ws.id=${ws.id}, ${ws._socket.remoteAddress}:${ws._socket.remotePort} #clients=${wss.clients.size}`);});
        ...
        

        【讨论】:

          猜你喜欢
          • 2022-01-15
          • 2017-08-17
          • 2014-07-13
          • 1970-01-01
          • 1970-01-01
          • 2022-01-17
          • 1970-01-01
          • 1970-01-01
          • 2011-01-23
          相关资源
          最近更新 更多