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

基于Node、Express与Socket.io的类棋类游戏匹配后统一重定向方案咨询

实现棋类游戏匹配与重定向的可行方案

我来帮你梳理一套能解决当前问题的实现思路,核心是把匹配请求排队和游戏阶段Socket连接拆分开,避免重定向导致的Socket断开问题,同时还原类似chess.com的匹配逻辑。

核心思路

点击PLAY按钮后,先让服务器把玩家加入匹配队列,不立即重定向;当队列凑齐2人时,服务器生成唯一GameID,主动通知两位玩家跳转至游戏页面;玩家进入游戏页面后,再建立新的Socket连接并加入对应游戏房间,启动游戏。

具体代码实现

1. 首页客户端代码(处理匹配请求)

点击PLAY时先建立匹配专用的Socket,发送匹配请求后等待服务器的匹配成功通知:

// 首页的JS代码
let matchSocket;

function play() {
  // 建立匹配阶段的Socket连接
  matchSocket = io();

  // 监听匹配成功的通知,收到后跳转游戏页面
  matchSocket.on("match_success", (gameInfo) => {
    window.location.href = `/game/${gameInfo.gameId}`;
    matchSocket.disconnect(); // 匹配完成,断开当前Socket
  });

  // 监听匹配超时/失败的情况(可选)
  matchSocket.on("match_timeout", () => {
    alert("匹配超时,请重试");
    matchSocket.disconnect();
  });

  // 发送加入匹配队列的请求
  matchSocket.emit("join_match_queue");
}

2. 服务器端核心逻辑(匹配队列+房间管理)

维护等待队列和游戏房间映射,处理匹配请求和游戏房间连接:

const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = new Server(server);

// 等待匹配的玩家队列(存储Socket实例)
const waitingPlayers = [];
// 游戏房间映射:key=GameID,value=房间信息
const gameRooms = new Map();

io.on('connection', (socket) => {
  // 处理玩家加入匹配队列
  socket.on('join_match_queue', () => {
    console.log(`玩家 ${socket.id} 加入匹配队列`);
    waitingPlayers.push(socket);

    // 检查队列是否凑齐2人
    if (waitingPlayers.length >= 2) {
      // 取出前两位玩家
      const player1 = waitingPlayers.shift();
      const player2 = waitingPlayers.shift();

      // 生成唯一GameID(时间戳+随机字符串保证唯一性)
      const gameId = `game_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;

      // 初始化游戏房间信息
      gameRooms.set(gameId, {
        players: [player1.id, player2.id],
        status: 'ready'
      });

      // 通知两位玩家匹配成功,发送GameID
      player1.emit('match_success', { gameId });
      player2.emit('match_success', { gameId });
      console.log(`匹配成功:游戏ID ${gameId},玩家 ${player1.id} & ${player2.id}`);
    }
  });

  // 处理游戏页面的Socket连接请求
  socket.on('join_game_room', (gameId) => {
    const room = gameRooms.get(gameId);
    if (!room) {
      socket.emit('error', '无效的游戏房间');
      window.location.href = '/';
      return;
    }

    // 让Socket加入对应游戏房间
    socket.join(gameId);
    room.status = 'playing';

    // 通知房间内所有玩家游戏开始
    io.to(gameId).emit('game_start', {
      gameId,
      players: room.players,
      firstPlayer: room.players[0]
    });
    console.log(`玩家 ${socket.id} 加入游戏房间 ${gameId}`);
  });

  // 处理玩家断开连接的异常情况
  socket.on('disconnect', () => {
    // 从匹配队列中移除断开的玩家
    const queueIndex = waitingPlayers.indexOf(socket);
    if (queueIndex !== -1) {
      waitingPlayers.splice(queueIndex, 1);
      console.log(`玩家 ${socket.id} 退出匹配队列`);
    }

    // 处理游戏中玩家断开的情况
    for (const [gameId, room] of gameRooms.entries()) {
      const playerIndex = room.players.indexOf(socket.id);
      if (playerIndex !== -1) {
        room.players.splice(playerIndex, 1);
        // 通知剩余玩家对手断开
        io.to(gameId).emit('opponent_disconnected');
        // 房间空了就删除
        if (room.players.length === 0) {
          gameRooms.delete(gameId);
        }
        break;
      }
    }
  });
});

// 托管静态页面(首页、游戏页面)
app.use(express.static('public'));

// 游戏页面路由
app.get('/game/:gameId', (req, res) => {
  res.sendFile(__dirname + '/public/game.html');
});

server.listen(3000, () => {
  console.log('服务器运行在 http://localhost:3000');
});

3. 游戏页面客户端代码(建立游戏Socket)

页面加载后获取URL中的GameID,连接Socket并加入游戏房间:

// 游戏页面的JS代码
const gameId = window.location.pathname.split('/')[2]; // 从URL路径提取GameID
const gameSocket = io();

gameSocket.on('connect', () => {
  // 发送加入游戏房间的请求
  gameSocket.emit('join_game_room', gameId);
});

// 监听游戏开始通知,初始化游戏逻辑
gameSocket.on('game_start', (data) => {
  console.log('游戏开始!', data);
  // 这里写棋盘渲染、回合逻辑等游戏代码
});

// 监听对手断开的通知
gameSocket.on('opponent_disconnected', () => {
  alert('对手已断开连接,游戏结束');
  window.location.href = '/';
});

// 监听错误提示
gameSocket.on('error', (msg) => {
  alert(msg);
  window.location.href = '/';
});

关键细节说明

  • 分离匹配与游戏Socket:匹配阶段用单独的Socket,匹配成功后跳转,游戏阶段重新建立Socket连接,避免重定向导致的连接中断。
  • 队列与房间管理:用数组维护等待队列,凑齐2人就生成房间;用Map存储游戏房间信息,方便后续游戏逻辑调用。
  • 异常处理:处理玩家在匹配中、游戏中断开的情况,保证队列和房间数据的准确性,同时给用户友好提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 18:27:31