首页 > 解决方案 > socket.io 不向房间内的其他人发送事件

问题描述

所以我试图弄清楚一个非常基本的socket.io应用程序发生了什么。我通过让 2 个不同的选项卡注册为不同的用户并尝试查看另一个选项卡是否获得事件来对此进行测试。前端都没有接收到其他事件,但服务器确实可以正确接收到事件。我的前端是 vue.js,我认为我的代理设置正确。我不确定为什么连接到同一个房间的房间 2 人不会收到基于文档的事件。

编辑:如果我删除 roomId 的 .to ,则消​​息将仅复制给发送消息的用户:/不确定这是否意味着什么。

Edit2:这与 rabbitmq 适配器不能用于中继消息有关。通过删除该中间件,一切正常。

Node.js 服务器

import http from 'http';
import express from 'express';
import { Server } from 'socket.io';
const adapter = require('socket.io-amqp');

const app = express();
const server = http.createServer(app);
const io = new Server(server,{
  path: '/socket'
});

io.adapter(adapter('amqp://localhost'));

app.get('/',(req,res) => {
    res.send('<h1>Hello World, Again</h1>')
});

io.on('connection',(socket) => {
  
  const query = socket.handshake.query;
  const userId = query['userId'] as string;
  const roomId = query['roomId'] as string;
  if(!userId || !roomId)
  { 
    console.log('failed to connect - userId or roomId is undefined');
    socket.disconnect(); 
    return;
  } 
  
  socket.join(roomId);
  console.log(`connection - user: ${userId}, room: ${roomId}`);

  socket.on('newValue',(e) => {
    socket.to(roomId).emit('newValue',{
      userId: userId,
      value: e.value
    });

    console.log(`roomId: ${roomId}, userId: ${userId}, value: ${e.value}`);
  });

  socket.on('disconnect',(reason)=> {
    socket.to(roomId).emit('userDisconnect',{
      userId: userId
    });

    console.log(`disconnect - user: ${userId}, room: ${roomId}`);
  });

  socket.to(roomId).emit('newUser',{
    userId
  });
});
 
const PORT = 3000;
server.listen(PORT,() => {
    console.log(`listening on http://localhost:${PORT}`);
});

管理套接字的 Vue.js 视图

<template>
  <div class="room">
    <div class="p-grid">
      <div class="p-col-12">
        <playing-card :value="selectedValue" class="center-card"/>
      </div>
    </div>
    <div class="p-grid">
      <div class="p-col-12">
        <value-selector @change="onValueChange"/>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import 'vue-router';
import { userStore } from '../store/index';
import { io, Socket } from 'socket.io-client';
import { Options, Vue } from 'vue-class-component';
import ValueSelector from '../components/ValueSelector.vue';
import PlayingCard from '../components/PlayingCard.vue';

@Options({ 
  components: { 
    PlayingCard, ValueSelector
  }
})
export default class Room extends Vue {
  
  selectedValue = -1;
  private socket!: Socket | null;

  private closeSocket(): void {
    if(this.socket)
    {
      this.socket.close();
      this.socket = null;
    }
  }

  private configureSocket(): void {
    if(!this.socket)
    {
      this.socket = io({
        path: '/socket',
        reconnection: true,
        reconnectionDelayMax: 10000,
        query: {
          roomId: this.$route.params.roomId as string,
          userId: userStore.getters.userId as string
        }
      });

      this.socket.on('newValue',(e)=>{
        console.log(e);
      });

      this.socket.onAny((eventName,e)=>{
        console.log(eventName);
        console.log(e);
      });
    }
  }

  mounted(): void {
    this.configureSocket();
  }

  onValueChange(values: any) {
    this.selectedValue = values.newVal;
    if(this.socket)
    {
      this.socket.emit('newValue', {
        value: this.selectedValue
      });
    }
  }

  unmounted(): void {
    this.closeSocket();
  }
}
</script>

<style lang="scss" scoped>
.center-card {
  margin: auto;
}
</style>

Vue.config.js

module.exports = {
    devServer: {
      proxy: {
        '/socket': {
          target: 'http://localhost:3000',
          changeOrigin: true,
          ws: true,
        }
      }
    }
  }

服务器日志

[nodemon] 2.0.7
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: ts,json
[nodemon] starting `ts-node src/index.ts`
listening on http://localhost:3000
connection - user: ls91m4il, room: 123, socketId: 86zN3g6maJu1z0OzAAAB
connection - user: pq68znqa, room: 123, socketId: xvKKzXhEbCrnz_l_AAAD
roomId: 123, userId: ls91m4il, socketId, 86zN3g6maJu1z0OzAAAB value: 5
roomId: 123, userId: pq68znqa, socketId, xvKKzXhEbCrnz_l_AAAD value: 13

对于 onAny 的事件响应或具有匹配命名事件的订阅,不会触发前端日志。

标签: node.jsvue.jssocket.io

解决方案


首先,socket.to(roomId).emit(...)将向 roomId 中的每个套接字发送消息,除了socket. 如果要发送到 中的所有套接字roomId,则可以使用io.to(roomId).emit(...). 我不清楚你是想发送到所有套接字还是除了发起者之外的所有套接字,所以我想确保你理解这方面的事情。

可以通过调试和日志记录来确保自己可以解决问题的事情:

  1. 确保两个客户端都连接到您的服务器并保持连接。记录和事件的实际socket.id情况将为您提供很好的了解。connectdisconnect

  2. 确保两个客户端都请求相同的roomId. 如果roomId每个的不同,那么只有一个套接字,roomId并且socket.to().emit()没有其他人可以发送到。

  3. 确保发送newValue消息的客户端没有发布导致页面重新加载的表单,因此旧套接字断开连接并创建新套接字。登录点 #1 应该确保您没有看到这种情况发生。我提到这一点主要是因为这是一个常见的错误,尽管我没有看到足够多的实际 HTML 页面来知道这对你来说是否是个问题。

  4. 记录服务器上的每条传入消息。不仅记录消息,还记录消息的socket.id来源。这将允许您重建整个消息流。

如果没有所有这些数据,我们就无法确定问题所在。这种类型的问题需要收集数据,然后跟踪线索,直到您可以更准确地看到正在发生的事情和没有发生的事情。如果我不得不冒险猜测,我想知道您是否对#1 或#2 有问题。


推荐阅读