我有一个Javascript GameClient,它使用SocketIO将消息发送到nodeJs服务器。多个用户可以分别打开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)
服务器(为简化起见,此处只是跟踪一个用户)
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在客户端和服务器上保持同步?
由于服务器还获得了重新连接事件,因此它如何知道哪个用户重新连接了?
答案 0 :(得分:2)
您的问题的答案非常简单:您需要一种方法来识别谁是谁。那不是socket.id
,因为正如您已经注意到的那样,它仅标识套接字,而不是用户。
因此,您需要某种身份验证机制。用户通过身份验证后,就可以重用其真实ID(无论是名称还是数据库中的整数都无关紧要)。然后在服务器端保留一对(true_id, socket_id)
对的集合。每当有消息发送给该用户时,您就将其广播到所有匹配的socket.io对象。
编辑:流程如下:
true_id
,客户端将其存储在某处。客户端还可以存储一些session_id
或其他机制,以便在断开连接的情况下允许他快速重新认证(注意:不存储凭据,这是一个安全问题)。(true_id, socket_id)
对(这是实现详细信息,此处应使用哪种数据结构,也许两个{}
对象就足够了) 。如果连接中断,则(true_id, socket_id)
条目将被删除。请注意,对于给定的true_id
,仍然可能还有其他socket_id
个在世。因此,这并不意味着用户已断开连接。这仅意味着该特定通道已死。socket_id
,他们只关心true_id
。当您要发送直接消息时,您发出的是{target_id: true_id, ...}
而不是客户端的{target_id: socket_id, ...}
。true_id
的此类消息时,它会检索所有(true_id, socket_id)
对并将消息传递给这些套接字的 all (请注意:也许您没有即使需要socket_id
,也可以在此处存储socket
个对象)。尽管这是一种业务逻辑:您是否允许每个用户多个连接?我会。这里有很多极端的情况(例如,客户端认为他已断开连接,而服务器认为他仍在连接等),但由于网络的性质,使此100%正确是不可能的。但是只要稍作努力,就有可能使它在99%的时间内都能正常工作。socket_id
,而旧的true_id
。让我再次强调一下:客户根本不在乎socket_id
。因为那不能识别他们。这仅标识一个通道。而且只有服务器在乎这些信息。