【问题标题】:How to emit from the default namespace to a custom namespace?如何从默认命名空间发射到自定义命名空间?
【发布时间】:2019-06-23 23:27:09
【问题描述】:

我有一个使用 Socket.io 的 React 应用程序。我使用默认命名空间/ 和自定义命名空间/test。我需要能够从一个命名空间发射到另一个命名空间,但我只设法让它从自定义到默认工作。

在客户端,事件监听器位于根目录 (App.js):

componentDidMount() {
  socket.on('test_listener', (data) => {
    // Some code
  })
}

使用redux初始化连接:

import socketIOClient from 'socket.io-client';

const initialState = {
  initializeSocket: socketIOClient('http://localhost:3000/'),
  ...
}

在服务器端,代码如下:

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

  socket.on('something', (data) => {

    io.of('/test').emit('test_listener', data); // Not working
    socket.broadcast.emit('test_listener', data); // Is working

  })

});

我确信io.of('namespace').emit('event_name', data); 应该可以正常工作,因为它在Socket.io's Emit cheatsheet 中已解决。并且客户端的事件监听器正在工作,因为我得到了 broadcast.emit 的一些东西。

另外,我可以使用以下代码从/test 发射到/

socket.on('join_namespace_test', data => {

  // [...] Some code to check if the namespace exists

  io.of('/test').on('connection', function (nspSocket) {

    nspSocket.on('do_something', (data) => {
      io.of('/').emit('another_action', data); // This one is working fine
    })

  })

  // [...] Some code to add the user to the namespace /test (switching from default)
}

知道为什么从//test 的发射不起作用吗? 我没有收到任何错误,没有任何反应。


编辑:经过进一步研究,我发现this post

您不能在现有连接上“切换”命名空间。建立连接后,您将连接到特定的命名空间,一旦建立,就无法更改。

为了确保问题不是来自项目的其他部分,我在小提琴上重现了这个问题。从默认到自定义的发出仍然不起作用,我还注意到broadcast 在客户端从默认切换到自定义然后返回默认时有一个奇怪的行为(它不应该发送给发件人) .

所以,考虑到问题可能是客户端在默认命名空间和自定义命名空间之间切换的方式而不是发射本身,也许房间会是一个更好的选择?或者我应该在切换之前找到一种方法来断开连接,但是否推荐?

【问题讨论】:

    标签: reactjs sockets socket.io


    【解决方案1】:

    经过大量测试,唯一有效的解决方案是在切换到另一个命名空间之前关闭之前的连接,然后再次调用套接字侦听器。当 socket.on 被初始化时,它只会在命名空间/连接被设置时起作用。 This post helped me to find the solution.

    对于一个简单的 redux 代码,解决方案可能如下所示(客户端,服务器端无需更改):

    var socket = io();
    listen();
    
    function switchNamespace(namespace) {
      socket.close(); // Close the current connection
      socket = io(namespace);
      listen();
    }
    
    function listen() {
      socket.on('test_listener', (data) => {
        // Some code
      })
    }
    

    附带说明,在默认 io.on('connection', ... 内部或外部初始化 io.of('/namespace').on('connection', ... 没有任何区别。

    在我们的例子中,这个解决方案不适合当前的项目结构,所以我们决定改用房间。

    【讨论】:

      【解决方案2】:

      你能告诉我连接是如何在客户端初始化的吗?

      应该是这样的:

      const socket = io('/test')
      

      如果您在 default 命名空间上广播某些内容,并且客户端接收到它,这意味着客户端连接到 / 命名空间,而不是 /test 之一。

      在客户端,我很确定您不能在同一个套接字上监听多个命名空间。如果你想这样做,你需要在客户端初始化多个套接字:

      const socketDefault = io()
      const socketTest = io('/test')
      

      编辑

      如果您只需要同时使用一个命名空间,您的另一个响应很好。

      然而,如果你想要一个真正的多命名空间处理,一个更好一点的代码可能如下:

      class Sockets {
      
        // Used as Handler for the proxy for `multi` method, should work, but didn't test it
        get (target, prop) {
          return (...args) => {
            const output = [];
            for(const alias of target._aliases) {
              output.push(target._sockets[alias][prop](...args))
            }
            return output;
          }
        }
      
        // Used to proxy methods, and only methods
        multi(aliases) {
          return new Proxy({ _sockets: this, _aliases: aliases }, this); // Use the above `get` for all undefined methods
        }
      
        addNamespace(alias, namespace) {
          if (this[alias]) {
            return false
          }
      
          Object.defineProperty(this, alias, {
            enumerable: true,
            writable: false,
            configurable: false,
            value: io(namespace)
          })
        }
      }
      
      const sockets = new Sockets()
      sockets.addNamespace('default')
      sockets.addNamespace('test', '/test')
      sockets.addNamespace('toto', '/toto')
      
      sockets.test.on('test_listener', (data) => {
        // Some code
      })
      
      sockets.toto.on('test_listener', (data) => {
        // Some code
      })
      
      sockets.multi(['test', 'toto', 'default']).on('common_listener', (data) => {
      
      })
      

      【讨论】:

      • 我用更多细节编辑了这个问题,我认为问题来自用户在命名空间之间切换的方式。在客户端,redux 正在处理套接字连接,当用户转到另一个命名空间时,状态会随着 reducer 发生变化。我添加了一个具有类似逻辑(但没有 redux)的小提琴来测试问题。
      • 我没有收到这个线程的通知,抱歉耽搁了,我不明白你为什么不打开多个套接字,如果你经常切换,我不认为关闭和重新开放是值得的……
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      • 2018-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多