【问题标题】:Sending message to a specific connected users using webSocket?使用 webSocket 向特定连接的用户发送消息?
【发布时间】:2013-04-23 05:50:18
【问题描述】:

我编写了一个向所有用户广播消息的代码:

// websocket and http servers
var webSocketServer = require('websocket').server;

...
...
var clients = [ ];

var server = http.createServer(function(request, response) {
    // Not important for us. We're writing WebSocket server, not HTTP server
});
server.listen(webSocketsServerPort, function() {
  ...
});

var wsServer = new webSocketServer({
    // WebSocket server is tied to a HTTP server. 
    httpServer: server
});

// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
...
var connection = request.accept(null, request.origin); 
var index = clients.push(connection) - 1;
...

请注意:

  • 我没有任何用户参考,只有一个连接。
  • 所有用户连接都存储在array 中。

目标:假设 Node.js 服务器想要向特定客户端 (John) 发送消息。 NodeJs 服务器如何知道 John 有哪个连接? Node.js 服务器甚至不认识 John。它所看到的只是连接。

所以,我相信现在,我不应该只通过他们的连接来存储用户,相反,我需要存储一个对象,其中将包含 userIdconnection 对象。

想法:

  • 当页面完成加载(DOM 就绪)- 建立到 Node.js 服务器的连接。

  • 当 Node.js 服务器接受连接时 - 生成一个唯一的字符串并将其发送到客户端浏览器。将用户连接和唯一字符串存储在对象中。例如{UserID:"6", value: {connectionObject}}

  • 在客户端,当此消息到达时 - 将其存储在隐藏字段或 cookie 中。 (用于将来对 NodeJs 服务器的请求)


当服务器要向 John 发送消息时:

  • 在字典中找到john的UserID,并通过对应的连接发送消息。

  • 请注意这里没有涉及到 asp.net 服务器代码(在消息机制中)。只有 NodeJs .*

问题:

这是正确的方法吗?

【问题讨论】:

    标签: javascript html node.js websocket


    【解决方案1】:

    这不仅是正确的方法,而且是唯一的方法。基本上每个连接都需要一个唯一的 ID。否则你将无法识别它们,就这么简单。

    现在你将如何表示它是另一回事。使用idconnection 属性创建一个对象是一个很好的方法(我肯定会这样做)。您也可以将id 直接附加到连接对象。

    还要记住,如果你想在用户之间进行通信,那么你也必须发送目标用户的 ID,即当用户 A 想向用户 B 发送消息时,显然 A 必须知道 B 的 ID。

    【讨论】:

    • @RoyiNamir 同样,您别无选择。浏览器将终止连接,因此您必须处理断开连接和重新连接。此外,您可能希望实现某种超时机制(仅在超时后销毁服务器端的包装对象)以避免发生这种情况时不必要的通知(假设您想通知其他用户断开连接)。
    • @RoyiNamir 至于两个唯一的名称:另一个想法是在服务器端保留对象SOCKETS = {};,并在给定ID的列表中添加一个套接字,即SOCKETS["John"] = [];和@987654328 @。您可能需要它,因为用户可以打开多个标签。
    • @freakish :小心,这只是单进程应用程序的一个很好的解决方案。如果您使用多个进程(或服务器)扩展您的应用程序,它们将不会共享相同的内存 - 因此使用本地存储对象的迭代将不起作用。最好直接迭代所有本地连接,将 UUID 存储在连接本身上。此外,对于扩展,我会推荐 Redis。
    • 您能告诉我如何使用连接对象向特定客户端发送消息吗?如上所述,我已将用户 ID 保存在连接对象中。我只想知道如何在从服务器向特定客户端发送消息时使用连接对象(我有该用户的 ID 和连接对象)。谢谢。
    • @AsishAP,这是一个很长的讨论...我建议您从阅读this article 开始只是为了获得方向,而不是搜索其他问题,here's one worth reading
    【解决方案2】:

    这是一个简单的聊天服务器私人/直接消息传递。

    package.json

    {
      "name": "chat-server",
      "version": "0.0.1",
      "description": "WebSocket chat server",
      "dependencies": {
        "ws": "0.4.x"
      }
    }
    

    server.js

    var webSocketServer = new (require('ws')).Server({port: (process.env.PORT || 5000)}),
        webSockets = {} // userID: webSocket
    
    // CONNECT /:userID
    // wscat -c ws://localhost:5000/1
    webSocketServer.on('connection', function (webSocket) {
      var userID = parseInt(webSocket.upgradeReq.url.substr(1), 10)
      webSockets[userID] = webSocket
      console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(webSockets))
    
      // Forward Message
      //
      // Receive               Example
      // [toUserID, text]      [2, "Hello, World!"]
      //
      // Send                  Example
      // [fromUserID, text]    [1, "Hello, World!"]
      webSocket.on('message', function(message) {
        console.log('received from ' + userID + ': ' + message)
        var messageArray = JSON.parse(message)
        var toUserWebSocket = webSockets[messageArray[0]]
        if (toUserWebSocket) {
          console.log('sent to ' + messageArray[0] + ': ' + JSON.stringify(messageArray))
          messageArray[0] = userID
          toUserWebSocket.send(JSON.stringify(messageArray))
        }
      })
    
      webSocket.on('close', function () {
        delete webSockets[userID]
        console.log('deleted: ' + userID)
      })
    })
    

    说明

    要对其进行测试,请运行 npm install 以安装 ws。然后,要启动聊天服务器,请在一个终端选项卡中运行node server.js(或npm start)。然后,在另一个终端选项卡中,运行wscat -c ws://localhost:5000/1,其中1 是连接用户的用户ID。然后,在第三个终端选项卡中,运行wscat -c ws://localhost:5000/2,然后,要从用户21 发送消息,请输入["1", "Hello, World!"]

    不足之处

    这个聊天服务器很简单。

    • 坚持

      它不会将消息存储到数据库,例如 PostgreSQL。因此,您要向其发送消息的用户必须连接到服务器才能接收它。否则,消息将丢失。

    • 安全

      这是不安全的。

      • 如果我知道服务器的 URL 和 Alice 的用户 ID,那么我可以冒充 Alice,即以她的身份连接到服务器,允许我接收她的新传入消息并将消息从她发送给任何用户身份证我也知道。为了使其更安全,请修改服务器以在连接时接受您的访问令牌(而不是您的用户 ID)。然后,服务器可以从您的访问令牌中获取您的用户 ID 并对您进行身份验证。

      • 我不确定它是否支持 WebSocket Secure (wss://) 连接,因为我只在 localhost 上测试过它,而且我不确定如何从 localhost 安全连接。

    【讨论】:

    • 有谁知道如何制作WebSocket Secure localhost connection
    • 这里也一样。但是如果你用 'ws.upgradeReq.headers['sec-websocket-key']' 替换 'upgradeReq...' 部分,它就可以工作。 (通过github.com/websockets/ws/issues/310
    • 在实践中启动 WS 的最佳示例!谢谢
    • 很好的例子!过去几天我一直在寻找这样的答案
    【解决方案3】:

    我想分享我所做的。希望不会浪费您的时间。

    我创建了包含字段 ID、IP、用户名、登录时间和注销时间的数据库表。当用户登录时 logintime 将是当前 unixtimestamp unix。当在 websocket 数据库中启动连接时,会检查最大的登录时间。它将是用户登录的。

    当用户注销时,它将存储当前的注销时间。用户将成为离开应用程序的人。

    每当有新消息时,都会比较 Websocket ID 和 IP,并显示相关的用户名。以下是示例代码...

    // when a client connects
    function wsOnOpen($clientID) {
          global $Server;
          $ip = long2ip( $Server->wsClients[$clientID][6] );
    
          require_once('config.php');
          require_once CLASSES . 'class.db.php';
          require_once CLASSES . 'class.log.php';
    
          $db = new database();
          $loga = new log($db);
    
          //Getting the last login person time and username
          $conditions = "WHERE which = 'login' ORDER BY id DESC LIMIT 0, 1";
          $logs = $loga->get_logs($conditions);
          foreach($logs as $rows) {
    
                  $destination = $rows["user"];
                  $idh = md5("$rows[user]".md5($rows["time"]));
    
                  if ( $clientID > $rows["what"]) {
                          $conditions = "ip = '$ip', clientID = '$clientID'  
    
                          WHERE logintime = '$rows[time]'";
                          $loga->update_log($conditions);
                 }
          }
          ...//rest of the things
    } 
    

    【讨论】:

      【解决方案4】:

      有趣的帖子(类似于我正在做的事情)。 我们正在制作一个 API(在 C# 中)将分配器与 WebSocket 连接起来,对于每个分配器,我们创建一个存储 WebSocket 和 DispenserId 的 ConcurrentDictionary ,使每个分配器可以轻松地创建一个 WebSocket 并在之后使用它而不会出现线程问题(调用特定函数在 WebSocket 上,例如 GetSettings 或 RequestTicket)。 您示例的不同之处在于使用 ConcurrentDictionary 而不是数组来隔离每个元素(从未尝试在 javascript 中这样做)。 最好的问候,

      【讨论】:

      • 这可以是评论而不是答案。请阅读规则。
      【解决方案5】:

      适用于使用 ws 版本 3 或更高版本的用户。如果您想使用@ma11hew28提供的答案,只需将此块更改如下。

      webSocketServer.on('connection', function (webSocket) {
        var userID = parseInt(webSocket.upgradeReq.url.substr(1), 10)
      
      webSocketServer.on('connection', function (webSocket, req) {
        var userID = parseInt(req.url.substr(1), 10)
      

      ws 包已将upgradeReq 移至请求对象,您可以查看以下链接了解更多详情。

      参考:https://github.com/websockets/ws/issues/1114

      【讨论】:

      • 你让我免于麻烦。
      猜你喜欢
      • 2020-05-06
      • 1970-01-01
      • 2013-03-19
      • 2016-02-16
      • 2014-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-24
      相关资源
      最近更新 更多