【问题标题】:reuse socket id on reconnect, socket.io, node.js在重新连接、socket.io、node.js 时重用套接字 id
【发布时间】:2013-08-20 02:49:30
【问题描述】:

是否可以重复使用 socket.id 或多次使用它?

假设用户在不同的浏览器选项卡中查看同一站点的多个页面。我想用一个socket.id,socket来处理它们。

如果用户收到通知,它应该在所有选项卡上弹出一个 socket.emit。

【问题讨论】:

    标签: node.js socket.io


    【解决方案1】:

    有可能

    从以前的回复日期来看,我认为在以前的 socket.io 版本中可能不可能,但我可以确认我在重新连接 socket.io 2.3.0 时成功地重用了套接字 ID。

    您只需要覆盖io.engine.generateId。无论该方法返回什么,都将是分配给套接字的 id。这是docs about generateId

    就我自己的实验而言,调用该方法有两种情况。在连接和重新连接期间。

    io.engine.generateId 方法接收原始请求对象作为参数,因此我们可以使用它来确定是要重用 id 还是获取新的 id。

    示例

    作为一个例子,我将展示如何重用从客户端发送的 id,或者在客户端不发送它时创建一个新的。 id 将在握手请求中作为查询参数socketId 发送。

    1。覆盖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 生成一个新的。特别是使用该库的原因是因为这就是原始方法的作用。在这里你可以找到source code

    2。发送连接请求信息

    一旦你有了它,你需要从客户端发送socketId 参数。这在the docs 中有描述。

    const socket = io.connect(process.env.WEBSOCKET_URL, {
      query: {
        socketId: existingSocketId || ''
      }
    })
    

    process.env.WEBSOCKET_URL 将是您的 Web 套接字正在侦听的 URL。

    请注意,这将在连接时起作用,但您可能希望在重新连接时更新查询。

    3。发送重连请求信息

    the docs 的同一部分,它解释了如何在重新连接之前更新查询参数。你只需要做这样的事情。

    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在连接丢失时会自行重新连接,可以让它回到同一个socket.io id吗?
    • @datdinhquoc 我认为在这种情况下它会自动重新使用 id。无论如何,您可以通过实现第 1 点和第 3 点手动强制它重用它。您需要在客户端上保存给定的 socketId 才能在第 3 点发送它。我建议使用类似 sessionStorage 的方法跨度>
    • @Daniel Reina ,我正在尝试效仿您的示例并覆盖 generateId()。但它不起作用,因为 searchparams 之后的代码由于某种原因永远无法到达。我在前后插入了一个日志语句。第二个不打印。同样在前端,我收到 400 bad request 作为错误。
    • @Atr0x 我会尝试返回一个硬编码的字符串。如果可行,我会查看io.engine.generateId 收到的req 参数。听起来您没有收到您期望收到的参数。
    【解决方案2】:

    您不能重复使用 Socket.IO 连接 ID,因为它们是在客户端-服务器握手期间创建的,但还有其他方法。我没有任何示例,但是您可以修改 Socket.IO 客户端以在执行握手时传递查询字符串。然后你可以告诉服务器根据查询字符串来处理客户端,然后用某个查询字符串获取所有客户端ID。

    您可以使用的另一种方法是使用命名空间。假设您有某种类型的会话系统,您可以创建一个特定于会话的命名空间,并将具有该会话 ID 的客户端直接连接到该命名空间。

    【讨论】:

    • 房间和命名空间有什么区别?
    【解决方案3】:

    多个网站?

    不,这是不可能的。我猜,如果您将这些网站打开到您的 web 应用程序中的 iframe 中,那将是可能的。

    另一种选择是构建一个打开套接字连接的浏览器插件。

    【讨论】:

    • 我想我会使用命名空间。谢谢。
    • 好吧,您说的是多个站点。网站不是页面。下次更具体;)
    猜你喜欢
    • 2012-12-27
    • 2015-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-27
    • 2011-11-22
    • 1970-01-01
    • 2020-11-09
    相关资源
    最近更新 更多