webSocketServer node.js如何区分客户端


73

我成功地将套接字与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发送通知。


如何确定哪些连接是“用户1”,并且是“用户2”?
lanzz

我不知道这是真的,我想在连接时向用户提供一种socketSession
Ajouve 2012年

3
嗯,一旦您知道了连接的用户身份(例如,当您的用户将其用户名作为消息发送时),便可以将对它的命名引用存储在字典中(例如connections[username] = ws),然后,在其他地方,您可以执行某些操作喜欢connections[username].send(message)
lanzz

如果您的需求不是只针对单个用户,而是将一组用户(可能是一组)分成“房间”,则可以使用套接字join()broadcast()方法。看到一些讨论在这里:stackoverflow.com/questions/6846174/...
meetamit

谢谢它工作良好,我所有的ws在数组中:)
Ajouve 2012年

Answers:


58

您可以简单地将用户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);
    }
}

1
如果来回发送客户端的唯一ID,则还可以获取消息的源客户端。
MehmetAVŞAR2014年

39
由于变量CLIENTS驻留在NodeJS的单个进程中,因此无法扩展此解决方案。即使我们通过派生多个进程来利用多个内核,但CLIENTS并不是共享内存分配,每个进程都维护自己的CLIENTS列表。因此,将需要随后的连接来与同一进程进行对话。
庆华2015年

10
@KhanhHua您能否提供一个可扩展的解决方案
vgoklani 2016年

7
您可以使用Redis pub-sub并创建一个“发布”通道,您拥有的每个服务器都将监听该通道。每当在该频道上发布消息时,每台服务器都会向其所有客户端广播。就那么简单。
Nepoxx

4
的文档WebSocketServer有一个clients跟踪当前的客户对你财产。该文档还提供了一个迭代该集合以向每个客户端发送广播消息的示例。
编码员

93

在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&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
+1为答案。添加支持它的参考资料: github.com/websockets/ws/issues/859
Maoritzio

太棒了,谢谢!我使用的是websocket库(而不是ws),所以我不得不使用req.resourceURL它包括一个已解析的查询字符串作为对象的一部分,因此您可以req.resourceURL.query.myCustomParam
Chris Broski

在TypeScript中,出现Property 'id' does not exist on type 'WebSocket'错误。有没有一种方法,而不必any这里描述的那样强制转换为?
奥马尔·沙拉基

9

Worlize服务器中的这段代码确实对我有很大帮助。即使您使用的是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()函数,如链接代码所示。

希望能有所帮助。


2
for in循环标准不同,循环将需要用于广播,因为(关联)数组中存在“空洞”。
Tobiq

9

这取决于您使用哪个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);


您能否提供任何包含用户名的代码链接?
johannesMatevosyan

在客户端上,当您发送一条消息时,您可以创建一个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将字符串转换回其对象。我已经使用此方法创建用户组,但是它也可以用于存储用户数组。
droid-zilla

我有类似的问题,但无法解决,您可以看看吗?stackoverflow.com/questions/35535700/...
johannesMatevosyan

@johannesMatevosyan如果您认为我的回答是正确的,可以请赞成。谢谢。我对您的小插曲进行了评论,发现了一个可能影响事物的拼写错误。
droid-zilla

当然可以,首先我需要明天检查一下。
johannesMatevosyan

8

您可以使用请求标头“ sec-websocket-key”

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

  //statements...
});

sec-websocket-key冲突如何?
SerG

4

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

var clientID = ws._socket._handle.fd;

当我打开一个新的浏览器选项卡时,我得到一个不同的数字。

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


13
我认为,如果一个用户离开而另一个用户加入,则文件描述符fd可以重用...本质上,它们可能会被回收
user772401 2015年

1
如果使用离开,则可以将其从会话中删除,并且即使新加入的会话具有相同的编号,会话表中也会有未填充的密钥
Anton Krug,2015年

1
可能存在以下问题:在断开连接后,_handle将会消失,并且您无法从表中将其删除,可能可以将句柄保存在ws中?
安东·克鲁格

3

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

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

您怎么知道Ultron.id是唯一的?我找不到与此相关的任何内容。
adelriosantiago

使用其他连接自行尝试。
rrkjonnapalli '18年

1
ultronwebsockets/ws在去年一月的5.1、5.0和4.1版本中作为依赖项被删除。使用这些(或更高版本)版本时,ws._ultron将不再存在。
Jochem Schulenklopper

2

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

ws://xxxxxxx:9000/userID/<<deviceId>>


0

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

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


0

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

const wss = ctx.app.ws.server
const { userId } = ctx.request.query

try{

   ctx.websocket.uid = userId

}catch(err){
    console.log(err)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.