如何从默认名称空间发射到自定义名称空间?

时间:2019-01-30 16:04:36

标签: reactjs sockets socket.io

我有一个使用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有一个奇怪的行为(它在不应该发射给发送者的同时发射给发送者t)。

因此,考虑到问题可能是客户端在默认名称空间和自定义名称空间之间切换的方式,而不是发出本身,实际上,房间可能是一个更好的选择?或者也许我应该找到一种在切换之前断开连接的方法,但是建议这样做吗?

2 个答案:

答案 0 :(得分:0)

您能告诉我客户端如何初始化连接吗?

应该是这样的:

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) => {

})

答案 1 :(得分:0)

经过大量测试,唯一有效的解决方案是在切换到另一个名称空间之前关闭先前的连接,然后再次调用套接字侦听器。初始化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.of('/namespace').on('connection', ...内部或外部初始化io.on('connection', ...并没有什么不同。

在我们的情况下,此解决方案不适合当前的项目结构,因此我们决定改用房间。