【问题标题】:How synchronise socketIO connection ID's on client and server?如何在客户端和服务器上同步 socketIO 连接 ID?
【发布时间】:2019-10-02 20:03:35
【问题描述】:

我有一个使用 SocketIO 将消息发送到 nodeJs 服务器的 javascript GameClient。多个用户可以单独打开GameClient,向服务器发送消息。

GameClient
GameClient  ---> NodeJS Server
GameClient

服务器可以使用io.to(socketid).emit() 向特定客户端发送消息。代码如下所示:

客户

this.socket = io({ timeout: 60000 })
this.socket.on('connect', () => Settings.getInstance().socketid = this.socket.id)
this.socket.on('reconnect', (attemptNumber:number) => console.log("reconnecting..."))

const json = JSON.Stringify({socketid:this.socket.id, name:"Old Billy Bob"})
this.socket.emit('user created', json)

SERVER(为清楚起见进行了简化,此处仅跟踪一位用户)

user = {}

io.on('connection', (socket) => {
    console.log('new connection')
    socket.on('disconnect', () => {
        console.log('user disconnected')
    });

    socket.on('user created', (json) => {
        user = JSON.parse(json)
    });
});

// demo code, send a message to our user
io.to(user.socketid).emit("message to one user")

问题

当客户端浏览器选项卡因任何原因变为非活动状态时,客户端会断开连接并重新连接并获取新的套接字连接 ID。这实际上在 Chrome 和 Safari 中经常发生。

服务器只知道旧的连接ID,所以现在它不能再发送直接消息了。如何保持客户端和服务器上的套接字连接 ID 同步?

既然服务器也得到了重连事件,那么它怎么知道是哪个用户重连的呢?

【问题讨论】:

  • 您还忘记了user = {} 在所有客户端之间共享。
  • user 变量不是共享的吗?它只保存在server.js 在节点中运行
  • 它在客户端之间共享,它是全局的。即如果客户端 A 发送它的集合,那么客户端 B 再次发送它的集合,如果 A 请求该值,它将成为 B 的数据。
  • 啊我明白你的意思,但我只是简化了那里的代码,是的,它应该是一组具有唯一 ID 的用户

标签: javascript node.js websocket socket.io


【解决方案1】:

您的问题的答案很简单:您需要一种方法来识别谁是谁。这不是socket.id,因为它只识别套接字,而不是用户,正如您已经注意到的那样。

所以你需要一些身份验证机制。一旦用户进行身份验证,您就可以重用他的真实身份(无论是简单的名称还是数据库中的整数都无关紧要)。然后在服务器端,您保留一组对 (true_id, socket_id)。每当消息到达该用户时,您就会将其广播到所有匹配的 socket.io 对象。

编辑:以下是流程:

  1. 客户端向服务器进行身份验证,服务器向他发送他自己的true_id,客户端将其存储在某处。客户端还可以存储一些session_id 或其他一些允许他在断开连接时快速重新验证的机制(注意:不要存储凭据,这是一个安全问题)。
  2. 服务器以双向、多值映射的形式跟踪(true_id, socket_id)对(这是一个实现细节,这里应该使用什么样的数据结构,也许两个{}对象就足够了)。如果连接终止,则删除 (true_id, socket_id) 条目。请注意,对于给定的true_id,可能还有其他一些socket_id 活着。因此,这并不意味着用户已断开连接。这只意味着这个特定的频道已经死了。
  3. 用户不关心socket_id,他们只关心true_id。当您想发送直接消息时,您发出的是 {target_id: true_id, ...} 而不是客户端的 {target_id: socket_id, ...}
  4. 当服务器接收到带有true_id 的此类消息时,它会检索所有(true_id, socket_id) 对并将消息传递给所有这些套接字(注意:也许你甚至不需要@987654336 @,您可以在这里简单地存储 socket 对象)。尽管这是一个业务逻辑:您是否允许每个用户有多个连接?我会。这里有很多边缘情况(例如,客户端认为他断开连接,但服务器认为他仍然连接等),不幸的是,不可能做到 100% 正确(由于网络的性质)。但只要稍加努力,就可以让它在 99% 的时间内正常工作。
  5. 如果连接中断,则您的客户端有责任自动重新连接并重新进行身份验证。在服务器端为旧的true_id 生成新的socket_id

让我再次强调这一点:客户根本不关心socket_id。因为这并不能识别他们。这仅标识一个通道。而且只有服务器关心这些信息。

【讨论】:

  • 好吧,我的用户也有唯一的 id,但我仍然不明白如何在重新连接事件后同时更新服务器端和客户端的套接字 id。我仍然需要套接字 ID 来使用 io.to(socketId).emit('hello there'); 发送消息
  • 感谢您的解释!那么,当客户端断开连接然后重新连接时,它必须手动将另一条消息发送回服务器,以便服务器可以再次将客户端的 id 与新的套接字 id 耦合?并且服务器必须维护所有用户+socketids的列表?
  • @Kokodoko 确实如此。这种“手动发送消息”正是身份验证步骤。由于安全原因,这一步也是必要的(它大大降低了冒充的风险)。是的,服务器必须维护一个列表/地图。
猜你喜欢
  • 2018-02-27
  • 1970-01-01
  • 2014-02-16
  • 1970-01-01
  • 1970-01-01
  • 2016-01-08
  • 1970-01-01
  • 1970-01-01
  • 2013-05-13
相关资源
最近更新 更多