You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js Socket.io双人游戏匹配队列冲突问题咨询

问题描述

我正在使用Node.js的Socket.io开发一款双人/多人游戏,实现了如下匹配系统:当socket触发find-match事件时,将等待用户加入列表;若列表中有等待用户,则取出进行匹配。我不确定当前实现方式是否正确,担忧当大量用户使用该项目时,waitPlayers列表会出现冲突问题。若存在该风险,应如何解决?

客户端代码

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<button id="match-btn" onclick="match()">Find Match</button>
<script>
    const socket = io("http://localhost:5000");

    function match() {
        socket.emit("find-match")
    }
</script>

服务端代码

const express = require("express");
const cors = require("cors");
const http = require('http');
const socketIo = require('socket.io');
const app = express();
app.use(cors());
const server = require('http').Server(app);
server.listen(5000, () => {
    console.log("Server is running on port : 5000")
});
const io = socketIo(server, {
    cors: {
        origin: "http://localhost:3000",
        methods: ["GET", "POST"],
        credentials: true
    }
});

let waitPlayers = [];
io.on("connection", (socket) => {
    console.log("connected");
    
    socket.on("find-match", (data) => {
        console.log(socket.id)
        
        // 检查用户是否已在等待列表
        if (waitPlayers.find(e => e.id == socket.id)) {
            console.log("user already exists") 
            return;
        }
        
        // 存在可匹配用户
        if (waitPlayers.length > 0) {
            const player = waitPlayers[0];
            // 删除第一个等待用户(原代码用pop()是错误的)
            waitPlayers.pop();
            console.log(waitPlayers);

            // 生成随机房间密钥
            const roomkey = Math.random() * 10
            console.log(roomkey)
            player.join(roomkey);
            socket.join(roomkey);

            // 通知房间内用户开始匹配
            io.to(roomkey).emit('start-match', "");

        } else {
            // 加入等待列表
            waitPlayers.push(socket);
            socket.emit("wait-match", "");
        }

    })
})

当前实现的问题

  1. 数组操作逻辑错误:代码中取出等待列表的第一个用户waitPlayers[0],但用pop()删除最后一个元素。如果等待列表中有多个用户(比如[玩家A, 玩家B]),新玩家触发匹配时,会取出玩家A但删除玩家B,导致玩家A残留在列表中,后续会被重复匹配,引发逻辑混乱。
  2. 未处理用户断开场景:如果用户在等待匹配时断开连接,其socket实例会残留在waitPlayers列表中,后续匹配时会尝试向无效连接发送事件,导致匹配失败。
  3. 多进程/分布式部署限制:当前waitPlayers是单进程内存中的数组,若后续扩展到多进程或多服务器,每个实例会维护独立的等待列表,无法实现全局用户匹配,出现匹配断层。

解决方案

1. 修复数组操作逻辑

将pop()替换为shift(),确保取出第一个等待用户的同时,从列表中删除该用户:

// 原错误代码
const player = waitPlayers[0];
waitPlayers.pop();

// 修改后代码
const player = waitPlayers.shift(); // 取出并删除列表第一个元素

2. 处理用户断开连接

在socket连接的disconnect事件中,从等待列表移除当前用户:

io.on("connection", (socket) => {
    console.log("connected");

    socket.on("find-match", (data) => {
        // ... 原有匹配逻辑 ...
    });

    // 用户断开时清理等待列表
    socket.on("disconnect", () => {
        waitPlayers = waitPlayers.filter(player => player.id !== socket.id);
    });
});

3. 解决多进程/分布式匹配问题

若需支持多进程或多服务器部署,需使用共享存储维护全局等待队列,推荐使用Redis(其操作天然原子性,支持多实例共享):

  • 安装Redis客户端:npm install redis
  • 核心思路:
    1. 用Redis的List结构存储等待用户的socket.id(而非socket实例,多进程无法共享内存对象)
    2. 当用户触发find-match时,先尝试从Redis队列中取出一个等待用户;若取出成功则创建房间匹配,否则将自己的socket.id加入队列
    3. 用户断开时,从Redis队列中移除对应的socket.id

4. 可选优化

  • 给等待用户设置超时时间,超过指定时间未匹配则自动从队列中移除,避免用户无限等待
  • 支持按用户等级、网络延迟等条件进行精准匹配,提升游戏体验

内容的提问来源于stack exchange,提问作者tokdev0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 13:01:00