【问题标题】:Setting Socket.io room variables设置 Socket.io 房间变量
【发布时间】:2020-12-11 07:52:38
【问题描述】:

我想在套接字房间变量中存储一些信息,但收到以下错误:UnhandledPromiseRejectionWarning: TypeError: Cannot set property 'host' of undefined

这是我的代码:

io.on('connection', (socket, next) => {
    socket.on('create game', async () => {
        console.log('Creating game...');
        socket.username = await generateUsername();

        socket.roomId = generateId();

        socket.join(socket.roomId);

        io.sockets.adapter.rooms[socket.roomId].host = socket.username;
        io.sockets.adapter.rooms[socket.roomId].isOpen = true;
        io.sockets.adapter.rooms[socket.roomId].players = [socket.username];

        console.log('Game created! ID: ', socket.roomId);
    });
}

如果我尝试记录 socket.roomId,它将返回类似 rBAhx0 的内容。当我登录io.sockets.adapter.rooms 时,我得到以下信息:

Map {
  'PX_o3Di9sp_xsD6oAAAB' => Set { 'PX_o3Di9sp_xsD6oAAAB' },
  'rBAhx0' => Set { 'PX_o3Di9sp_xsD6oAAAB' }
}

但是,当我尝试登录 io.sockets.adapter.rooms[socket.roomId] 时,它会返回 undefined。我该如何解决这个问题?

【问题讨论】:

    标签: javascript node.js express websocket socket.io


    【解决方案1】:

    Socket.io 在最新版本中进行了一些重大更改,无法像以前那样访问房间。它将很多对象和数组更改为 Maps 和 Sets,您可以在发布的日志中看到它。

    Set 对象是值的集合。您可以按插入顺序遍历集合的元素。 Set 中的一个值只能出现一次;它在 Set 的集合中是独一无二的。

    Map 对象保存键值对并记住键的原始插入顺序。任何值(对象和原始值)都可以用作键或值。

    访问地图的属性与访问普通对象的属性不同。示例:

    const myMap = new Map();
    myMap.set("foo", "bar");
    console.log(myMap["foo"]) // undefined
    console.log(myMap.get("foo")) // bar

    这同样适用于 Set,但是在您的情况下,特别是查询这个 Set 可能是一种错误的方法,因为这个集合只包含一个 id 集合,而不是实际的房间对象。即使您要从 Set 中获取一个值,您也无法访问它的属性(host、isOpen 和 player),因为它只是一个字符串。

    恐怕 3.0 版无法直接访问所有房间的列表。但是适配器现在有一个属性socketRooms,可以用来代替它。

    为了更容易访问套接字的房间,您应该像这样访问它们:

    io.sockets.adapter.socketRooms(socketId);
    

    但是,这仍然只是返回一个字符串列表。

    解决这个问题最简单的方法是在连接范围之外创建一个外部变量。

    const rooms = {};
    
    io.on('connection', (socket, next) => {
        socket.on('create game', async () => {
            console.log('Creating game...');
            socket.username = await generateUsername();
    
            socket.roomId = generateId();
    
            socket.join(socket.roomId);
    
            if (!rooms[socket.roomId]) rooms[socket.roomId] = {};
            rooms[socket.roomId].host = socket.username;
            rooms[socket.roomId].isOpen = true;
            rooms[socket.roomId].players = [socket.username];
    
            console.log('Game created! ID: ', socket.roomId);
        });
    }
    

    【讨论】:

    • 感谢您的快速回复和澄清!你会建议我做什么来存储变量?我不想使用数据库,而是想将信息存储在房间本身中(例如游戏是否打开、主持人是谁(用户名)和玩家用户名等信息)。有没有其他方法可以存储这些数据,以便同一个房间的其他套接字可以访问它?
    【解决方案2】:

    正如您在自己的日志中看到的,io.sockets.adapter.rooms 是一个 Map 对象(在 socket.io 的更高版本中),而不是普通对象。这意味着使用.get() 访问房间,而不是使用简单的索引(例如您正在使用的索引):

    io.sockets.adapter.rooms[socket.roomId].host = socket.username;
    

    这根本行不通,因为地图上没有普通的socket.roomId 属性。

    如果你想得到房间对象,你必须这样做:

    let roomObj = io.sockets.adapter.rooms.get(socket.roomId);
    roomObj.host = socket.username;
    

    在我看来,我不会直接弄乱房间物品。我们已经知道 socket.io 之前已经改变了他们的设计,将任何试图使用它们的代码呈现为损坏并需要重新设计。相反,我可能只是创建我自己的数据结构来存储我自己的数据,然后我不会依赖于 socket.io 如何改变他们的设计的突发奇想。直接访问房间对象不是 socket.io 的承诺设计或承诺 API。他们可以随时更改其工作方式,而且事实上,最近已经更改了。

    【讨论】:

    • 感谢您的快速回复和澄清!你会建议我做什么来存储变量?我不想使用数据库,而是想将信息存储在房间本身中(例如游戏是否打开、主机是谁(用户名)和玩家用户名等信息)。有没有其他方法可以存储这些数据,以便同一个房间的其他套接字可以访问它?
    • @MilanFerus-Comelo - 您可以使用 roomId 键创建自己的对象,该键跟踪您自己的房间数据,然后任何套接字都可以访问那里的数据。因此,不要使用io.sockets.adapter.rooms,而是使用您自己的对象作为父对象。
    【解决方案3】:

    在从 Socket.io 将 Array 更改为 Map 的重大更新之后。 一个简单的解决方案是改变:

        io.sockets.adapter.rooms[socket.roomId].host = socket.username;
    

    到:

        io.sockets.adapter.get(socket.roomId).host = socket.username;
    

    【讨论】:

      猜你喜欢
      • 2021-03-02
      • 2019-05-13
      • 1970-01-01
      • 2019-03-02
      • 2011-12-02
      • 1970-01-01
      • 2014-09-07
      • 2018-07-05
      • 1970-01-01
      相关资源
      最近更新 更多