Socket.io 自定义客户端 ID

43

我正在使用socket.io制作一款聊天应用程序,我想使用自定义客户端ID而不是默认的ID (8411473621394412707, 1120516437992682114)。是否有任何方法可以在连接时发送自定义标识符,或者只是使用某些内容来跟踪每个ID的自定义名称?谢谢!


最佳解决方案在这里(客户端可以通过socket.handshake.query在连接时标识自己或命名自己)。https://dev59.com/9V8f5IYBdhLWcg3wD_Av#39711590 - Manohar Reddy Poreddy
11个回答

55

你可以在服务器上创建一个数组,并将自定义对象存储在其中。例如,你可以将Socket.io创建的ID和每个客户端发送到服务器的自定义ID存储在其中:

var util = require("util"),
    io = require('/socket.io').listen(8080),
    fs = require('fs'),
    os = require('os'),
    url = require('url');

    var clients =[];

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

        socket.on('storeClientInfo', function (data) {

            var clientInfo = new Object();
            clientInfo.customId         = data.customId;
            clientInfo.clientId     = socket.id;
            clients.push(clientInfo);
        });

        socket.on('disconnect', function (data) {

            for( var i=0, len=clients.length; i<len; ++i ){
                var c = clients[i];

                if(c.clientId == socket.id){
                    clients.splice(i,1);
                    break;
                }
            }

        });
    });

在这个例子中,你需要从每个客户端调用storeClientInfo函数。

<script>
    var socket = io.connect('http://localhost', {port: 8080});

    socket.on('connect', function (data) {
        socket.emit('storeClientInfo', { customId:"000CustomIdHere0000" });
    });
</script>

希望这可以帮到你。


我们能否从客户端设置CustomId,使用URL查询字符串参数id? - Vijaysinh Parmar
1
不要更改套接字ID为自己选择的ID,这会完全破坏Socket.io房间系统。它将默默失败,您将不知道为什么客户端无法接收消息。 - user5536767
4
我建议使用Redis而不是变量。这样可以更好地提高可扩展性,如果服务器由于错误崩溃,您也不会丢失数据。 - Mohit Gangrade
问题不在于可扩展性,这只是一种管理ID的方式。 - oscarm

20

要设置自定义的socket id,必须覆盖generateId函数。可以使用Socket.io服务器对象的eioengine属性来管理此操作。

一个简单的例子:

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

io.engine.generateId = function (req) {
    // generate a new custom id here
    return 1
}

io.on('connection', function (socket) {
    console.log(socket.id); // writes 1 on the console
})

看起来它已经被处理了。

必须记住,套接字ID必须是不可预测的和唯一的值,同时考虑安全性和应用程序操作!

额外内容:如果因为您在generateId方法上的强烈进程而返回undefined作为socket.id,则可以使用async/await组合来解决此问题,在node.js 7.6.0及更高版本中, 应更改node_modules/engine.io/lib/server.js文件的handshake方法如下:

当前内容:

// engine.io/lib/server.js

Server.prototype.generateId = function (req) {
  return base64id.generateId();
};

Server.prototype.handshake = function (transportName, req) {
  var id = this.generateId(req);
  ...
}

新:

// function assignment

io.engine.generateId = function (req) {
  return new Promise(function (resolve, reject) {
    let id;
    // some intense id generation processes
    // ...
    resolve(id);
  });
};


// engine.io/lib/server.js

Server.prototype.handshake = async function (transportName, req) {
  var id = await this.generateId(req);
  ...
}

注意:Engine.io v4.0中,generateId方法将接受一个回调函数。因此,不需要更改handshake方法,仅替换generateId方法就足够了。例如:

io.engine.generateId = function (req, callback) {
  // some intense id generation processes
  // ...
  callback(id);
};

2
为什么这个 ID 不可预测很重要?客户端可以在连接时指定自己的 ID 吗?如果不能,我不明白为什么这是必要的。 - user3916570
实际上这取决于你想要什么。Q1-答案:可预测的ID可能会导致安全问题。Q2-答案:是的,它们可以。但服务器不知道哪个客户端在做什么。 - efkan
1
必须记住,套接字ID必须是不可预测的...考虑到安全性...可能会导致安全问题...你能举个安全问题的例子吗?据我所知,socket.io和engine.io文档中没有提到这一点。你是自己编造的吗? - spinkus
兄弟,如果我想从前端传递参数(值),那么如何传递并在这里访问。请帮忙。 - Rohit Nishad
1
@spinkus 我想这是因为如果你能预测某人的socketId,你就可以欺骗服务器认为你是那个人,并获得他们所有的数据访问权限。 - Ryan Soderberg
1
@RohitNishad 我已经实现了。你需要在从客户端连接时发送一个带有查询的对象作为第二个参数,像这样:{ query: { foo: 'foo' } }。然后在服务器上,你必须通过解析 req.url 来获取它,就像这里描述的那样:https://nodejs.org/api/url.html#url_constructor_new_url_input_base,像这样:const parsedUrl = new url.URL(req.url, host)。一旦你解析了 URL,你就可以像这样访问查询参数:parsedUrl.searchParams.get('foo')。如果你有一个 stack overflow 的问题,请提供链接,这样解释起来会更容易。 - Daniel Reina

8
在最新的 socket.io(版本1.x)中,您可以像这样做:
socket  = io.connect('http://localhost');

socket.on('connect', function() {
    console.log(socket.io.engine.id);     // old ID
    socket.io.engine.id = 'new ID';
    console.log(socket.io.engine.id);     // new ID
});

有人能确认一下吗?在我看来,这似乎是一个更好的解决方案。 - Sami Boudoukha
1
@SamiBoudoukha,socket.id字段现在是只读的,在socket.io v4中无法设置任何值。 - cekeriya
@Salvador Dali,我们可以在客户端使用这个(socket.io.engine.id)吗? - maranR

6

这将适用于2.2.0版本及以上的Socket.IO

要设置自定义Socket Id,必须覆盖generateId函数。

一个简单的示例:

服务器端

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

io.use((socket, next) => {
  io.engine.generateId = () => {
    // USE ONE OF THESE
    socket.handshake.query.CustomId; // this work for me
    // return socket.handshake.query.CustomId;
  }
  next(null, true);
});

io.on('connection', function (socket) {
    console.log(socket.id);
})

客户端

io.connect(URL, { query: "CustomId = CUSTOM ID IS HERE" })

注意:请记住,套接字 ID 必须是唯一的值。


1
非常感谢您的回答,但是我遇到了一个问题,花费了我几个小时才解决。检索到的CustomId没有返回,请通过添加return语句或删除大括号以进行自动返回来更正此答案。 - AbdulGafar Olamide Ajao
在哪里需要添加返回语句? - Rohit Nishad
在 'socket.handshake.query.CustomId' 之前。 - AbdulGafar Olamide Ajao
嗨,我尝试了这段代码,但似乎对我不起作用。我添加了一个新的问题:https://stackoverflow.com/questions/65833164/socket-io-my-middleware-for-custom-id-does-not-works 如果您能帮助我,谢谢! - Bertrand PETIT

6

我会使用对象作为哈希查找 - 这样可以避免循环数组的操作

var clients = {};
clients[customId] = clientId;

var lookup = clients[customId];

6
不要更改套接字 ID 为自己选择的 ID,这会完全破坏 Socket.io 的房间系统。它会无声失败,你将无法知道为什么客户端没有收到消息。

5
为什么不使用更简单的解决方案,它不需要维护连接客户端的数组,也不会覆盖内部套接字ID?
io.on("connection", (socket) => {
    socket.on('storeClientInfo', (data) => {
        console.log("connected custom id:", data.customId);
        socket.customId = data.customId;
    });

    socket.on("disconnect", () => {
        console.log("disconnected custom id:", socket.customId);
    })
});

客户端

let customId = "your_custom_device_id";
socket.on("connect", () => {
    socket.emit('storeClientInfo', { customId: customId });
});

4
或者您可以覆盖套接字ID,像这样:

或者您可以覆盖套接字ID,像这样:

io.on('connection', function(socket){

      socket.id = "YOUR_CUSTOM_ID";
});

您可以在数组下面看到:
io.sockets.sockets

1
我怀疑这是更好的解决方案,特别是在需要使用多个进程进行扩展时。 - James Westgate
1
这个可以改变id,但是在像io.sockets.connected[socketid]这样的情况下,套接字仍然被其原始id引用。 - RossBille
2
覆盖现有的socket.id会导致socket.io在维护自己的数据结构时出现问题。这不是一个好的解决方案。只需为自定义ID使用不同的属性名称即可。 - jfriend00
socket.id字段现在是只读的,在socket.io v4中不能设置任何值。 - cekeriya

2
使用这个 2.2.0 版本的 Socket.IO,你可以实现这一点。最初的回答。
io.use((socket, next) => {
  io.engine.generateId = () => socket.handshake.query.token;
  next(null, true);
});

2
可以将自定义ID(例如用户ID)以对象格式存储,而不是使用循环语句,这样可以提高连接、断开连接和检索套接字ID以进行发射的性能。
 var userId_SocketId_KeyPair = {};
 var socketId_UserId_KeyPair = {};

_io.on('connection', (socket) => {
    console.log('Client connected');
    //On socket disconnect
    socket.on('disconnect', () => {
        // Removing sockets
        let socketId = socket.id;
        let userId = socketId_UserId_KeyPair[socketId];
        delete socketId_UserId_KeyPair[socketId];
        if (userId != undefined) {
            delete userId_SocketId_KeyPair[userId];
        }
        console.log("onDisconnect deleted socket with userId :" + "\nUserId,socketId :" + userId + "," + socketId);
    });

    //Store client info
    socket.on('storeClientInfo', function (data) {
        let jsonObject = JSON.parse(data);
        let userId = jsonObject.userId;
        let socketId = socket.id;
        userId_SocketId_KeyPair[userId] = socketId;
        socketId_UserId_KeyPair[socketId] = userId;
        console.log("storeClientInfo called with :" + data + "\nUserId,socketId :" + userId + "," + socketId);
    });
`

你应该为你的解决方案添加一些细节以提高答案质量。(包括为什么和如何) - hering
1
他提到它可以提高性能,相比于数组,你可以通过字符串值访问元素(代价是大约需要双倍的存储空间)。我正在寻找一个快速而简单的解决方案,并在这里找到了它。 - beyondtdr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接