是否可以重复使用 socket.id 或多次使用它?
假设用户在不同的浏览器选项卡中查看同一站点的多个页面。我想使用一个 socket.id, socket 来处理它们。
如果用户收到通知,它应该在所有选项卡上弹出一个 socket.emit。
从以前的回复日期来看,我认为在以前的 socket.io 版本中可能不可能,但我可以确认我在重新连接 socket.io 2.3.0 时成功地重用了套接字 ID。
你只需要覆盖io.engine.generateId
. 无论该方法返回什么,都将是分配给套接字的 id。这是有关 generateId 的文档。
就我自己的实验而言,调用该方法有两种情况。在连接和重新连接期间。
该方法io.engine.generateId
接收原始请求对象作为参数,因此我们可以使用它来确定是否要重用 id 或获取新的 id。
作为一个例子,我将展示如何重用从客户端发送的 id,或者在客户端不发送它时创建一个新的。id 将作为查询参数在握手请求中发送socketId
。
io.engine.generateId
首先,您需要覆盖io.engine.generateId
,这是分配 ID 的方法。在服务器上,您需要执行类似的操作。
const url = require('url')
const base64id = require('base64id')
io.engine.generateId = req => {
const parsedUrl = new url.parse(req.url)
const prevId = parsedUrl.searchParams.get('socketId')
// prevId is either a valid id or an empty string
if (prevId) {
return prevId
}
return base64id.generateId()
}
这样,每当您socketId
在握手请求中发送查询参数时,它将被设置为套接字 id。如果您不发送它,您将使用base64id
. 特别是使用该库的原因是因为这就是原始方法的作用。在这里你可以找到源代码。
一旦你有了它,你需要socketId
从客户端发送参数。这在文档中有所描述。
const socket = io.connect(process.env.WEBSOCKET_URL, {
query: {
socketId: existingSocketId || ''
}
})
process.env.WEBSOCKET_URL
将是您的网络套接字正在侦听的 URL。
请注意,这将在连接时起作用,但您可能希望在重新连接时更新查询。
在文档的同一部分,它解释了如何在重新连接之前更新查询参数。你只需要做这样的事情。
socket.on('reconnect_attempt', () => {
socket.io.opts.query = {
socketId: existingSocketId || ''
}
});
就像这样,只要它是从客户端发送的,您就会重用相同的套接字 id。
信任从客户端发送的信息来分配套接字 ID 可能是个坏主意。我建议发送一个加密签名的有效负载,将该有效负载存储在客户端中,并在连接和重新连接时将其发送回服务器。这样,服务器可以通过验证签名来检查有效负载是否可信。
使用上面的相同示例,我们将向客户端发送类似这样的内容,也许是.on('connect')
:
{
socketId: 'foo',
signature: SHA_256('foo' + VERY_SECRET_PASSWORD)
}
客户端将存储该有效负载并在连接或重新连接时将其发送回,就像我们socketId
之前发送的方式一样。
一旦服务器接收到签名的有效载荷,io.engine.generateId
我们可以在内部检查有效载荷中的签名是否与我们使用 ID 和VERY_SECRET_PASSWORD
.
您不能重用 Socket.IO 连接 ID,因为它们是在客户端-服务器握手期间创建的,但有其他方法。我没有任何示例,但是您可以修改 Socket.IO 客户端以在执行握手时传递查询字符串。然后您可以告诉服务器根据查询字符串处理客户端,然后获取具有特定查询字符串的所有客户端 ID。
您可以使用的另一种方法是使用命名空间。假设您有某种类型的会话系统,您可以创建一个特定于会话的命名空间,并将具有该会话 ID 的客户端直接连接到该命名空间。
多个站点?
不,那是不可能的。我猜,如果您将这些网站打开到您的 web 应用程序中的 iframe 中,那将是可能的。
另一种选择是构建一个打开套接字连接的浏览器插件。