nodejs socket.io问题 期望socket.on('a')执行两次 实际执行了六次?

新手上路,请多包涵

nodejs socket.io问题

socket.io 服务器端

socket.on('a', () => {
  console.log('打开了五个socket.io客户端,并且emit("a"),这里会被调用六次')
  socket.emit('b')
  socket.emit('c')
})

socket.io 客户端

socket.on('b', () => {
  console.log('这里会被调用一次')
  socket.emit('a', { from: 'onB' })
})
socket.on('c', () => {
  console.log('这里也会被调用六次,这里是正常的,因为on("a")被调用了六次')
  console.log('c')
})
socket.emit('a', { id: `${Math.random()}`.slice(2) })

问题出在打开了五个socket.io客户端页面,在socket.emit('a')的时候,console.log('c')打印了6次。如何让只打印两次

在socket.io 服务器端新增lock

const lock = {}
socket.on('a', (info) => {
  if (lock[info.id]) return;
  lock[info.id] = true;
  // 这里console.log(info)的时候info里面的${id}变成了true
  // 但是ock[info.id]却一直是undefined,导致无法枷锁过滤掉其他四次的错误的调用
  if (info.from === 'onB') socket.emit('b')
  socket.emit('c')
})

期望socket.on('a')执行两次,最终还是执行了四次,求解决方案

阅读 484
avatarAI BotBETA

解决方案

问题出在你尝试使用 info.id 来作为锁定的键,但在你的代码中,socket.emit('a') 在客户端初始化时调用时并没有传递 id 字段,只有在 socket.on('b') 的回调中调用 socket.emit('a', { from: 'onB' }) 时才传递了 from 字段。因此,第一次从客户端触发的 socket.emit('a') 没有 id,导致 lock[info.id] 无法正确工作。

此外,你需要在服务器端处理所有来自客户端的 socket.emit('a') 调用,无论它们是否包含 id 字段。你需要确保对于每个客户端的初始 socket.emit('a') 调用(不带 id),以及后续可能由其他事件触发的 socket.emit('a')(带 id),都能被正确处理。

解决方案步骤:

  1. 修改客户端代码:确保每次 socket.emit('a') 都带有唯一的 id
  2. 修改服务器端代码:使用 socket.id(Socket.IO 为每个连接生成的唯一标识符)作为锁定的键,而不是依赖客户端传递的 id

修改后的代码

客户端代码

socket.on('b', () => {
  console.log('这里会被调用一次')
  socket.emit('a', { from: 'onB', id: `${Math.random()}`.slice(2) })
})

// 初始化时也传递一个唯一的 id
socket.emit('a', { id: `${Math.random()}`.slice(2) })

服务器端代码

const lock = {};

socket.on('a', (info) => {
  const clientId = socket.id; // 使用 socket.id 作为唯一标识符
  const eventId = info.id || clientId + '_initial'; // 对于没有 id 的情况,生成一个唯一的标识符

  if (lock[eventId]) return; // 如果已经处理过,则直接返回

  lock[eventId] = true; // 锁定事件

  if (info.from === 'onB') {
    socket.emit('b');
  }

  socket.emit('c');

  // 可选:在适当的时候清理锁,以避免内存泄漏
  // 例如,可以在 socket 断开连接时清理
  socket.on('disconnect', () => {
    delete lock[clientId + '_initial']; // 清理初始事件的锁
    // 如果有必要,也可以遍历 lock 对象,清理与该客户端相关的其他锁
  });
});

解释

  • 客户端:每次 emit('a') 时都传递一个唯一的 id
  • 服务器端:使用 socket.id 和传递的 id(如果有的话)来生成一个唯一的 eventId,用于锁定。如果 info 中没有 id,则使用 socket.id + '_initial' 作为标识符。
  • 锁管理:在适当的时候(如 socket 断开连接时)清理锁,以避免内存泄漏。

这样,无论客户端如何触发 socket.emit('a'),服务器端都能正确地处理,并且确保每个事件只被处理一次。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏