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", ""); } }) })
当前实现的问题
- 数组操作逻辑错误:代码中取出等待列表的第一个用户
waitPlayers[0],但用pop()删除最后一个元素。如果等待列表中有多个用户(比如[玩家A, 玩家B]),新玩家触发匹配时,会取出玩家A但删除玩家B,导致玩家A残留在列表中,后续会被重复匹配,引发逻辑混乱。 - 未处理用户断开场景:如果用户在等待匹配时断开连接,其socket实例会残留在
waitPlayers列表中,后续匹配时会尝试向无效连接发送事件,导致匹配失败。 - 多进程/分布式部署限制:当前
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 - 核心思路:
- 用Redis的List结构存储等待用户的
socket.id(而非socket实例,多进程无法共享内存对象) - 当用户触发
find-match时,先尝试从Redis队列中取出一个等待用户;若取出成功则创建房间匹配,否则将自己的socket.id加入队列 - 用户断开时,从Redis队列中移除对应的
socket.id
- 用Redis的List结构存储等待用户的
4. 可选优化
- 给等待用户设置超时时间,超过指定时间未匹配则自动从队列中移除,避免用户无限等待
- 支持按用户等级、网络延迟等条件进行精准匹配,提升游戏体验
内容的提问来源于stack exchange,提问作者tokdev0
相关产品推荐
相关产品推荐

