如何在多页面维持同一Socket连接?卡牌游戏开发遇阻
多玩家卡牌游戏Socket连接复用问题解决方案
问题描述
开发多玩家房间制卡牌游戏时,遇到Socket连接复用问题:
- 大厅页面调用
io()创建Socket连接,用于触发创建/加入房间事件 - 跳转至房间页后,导入连接时会重新执行
io()生成新连接 - 新连接未加入原房间,无法接收
server:newConnection事件,导致老玩家看不到新加入的玩家
解决方案
方案1:传递房间编码,在房间页重新加入对应房间
页面跳转后JS会重新加载,原Socket连接会断开,因此需要在房间页拿到房间编码后,主动让新连接加入房间,同时服务端补充逻辑同步房间内玩家列表。
修改客户端大厅代码
跳转房间页时,把房间编码存入sessionStorage:
const socket = io(); $("#createParty").click(() => { const code = newCode(); // 存储房间编码到sessionStorage sessionStorage.setItem("roomCode", code); socket.emit("client: newParty", {userName: sessionStorage.getItem("userName"), code: code}); window.location.href = "/room"; }); $("#connectParty").click(() => { const codeConnect = $("#txt-codeConnect").val(); // 存储房间编码到sessionStorage sessionStorage.setItem("roomCode", codeConnect); socket.emit("client: tryConnectParty", {userName: sessionStorage.getItem("userName"), codeConnect: codeConnect}); window.location.href = "/room"; }); export default socket;
修改客户端房间页代码
导入Socket后,主动发送加入房间的请求,并请求当前房间玩家列表:
import socket from "/main.js"; const userName = sessionStorage.getItem("userName"); const roomCode = sessionStorage.getItem("roomCode"); $("#txt-userName").html(`<b class='text-white'>${userName}</b>`); $("#txt-codeRoom").html(`<b>${roomCode}</b>`); var divPlayers = $("#players"); // 主动加入房间 socket.emit("client: joinRoom", { userName, roomCode }); // 接收玩家加入事件 socket.on("server: newConnection", (userName) => { divPlayers.append(createCard(userName)); }); // 接收房间现有玩家列表 socket.on("server: roomPlayers", (players) => { // 清空现有内容,添加所有玩家 divPlayers.empty(); players.forEach(player => { divPlayers.append(createCard(player)); }); }); function createCard(user) { return ` <div class="card d-inline-flex text-center" style="width: 300px;"> <div class="card-body"> <span class="h1 text-dark">${user}</span> </div> <img class="card-img-bottom" src="img/avatar.png" alt="avatar"> </div> `; }
修改服务端代码
新增client: joinRoom事件处理,以及同步玩家列表的逻辑:
// Import for server... import { Server as WebSocketServer } from "socket.io"; import http from "http"; import app from "./app.js"; const server = http.createServer(app); const io = new WebSocketServer(server); var rooms = []; export var usersRoom = []; // Events socket... io.on("connection", (socket) => { console.log("New conection:", socket.id); socket.on("client: newParty", (data) => { // data = {userName, code} const code = data.code; const roomId = "room-"+ code; socket.join(roomId); rooms.push({ room: code, users: [data.userName] }); }); socket.on("client: tryConnectParty", (data) => { // data = {userName, codeConnect} const code = data.codeConnect; const roomId = "room-"+ code; for (let i = 0; i < rooms.length; i++) { if (rooms[i].room == code) { const userName = data.userName; socket.join(roomId); rooms[i].users.push(userName); usersRoom = rooms[i].users; // 发送给房间内其他玩家 socket.to(roomId).emit("server: newConnection", userName); // 通知当前玩家房间内的所有玩家 socket.emit("server: roomPlayers", rooms[i].users); break; } } }); // 新增:处理房间页主动加入房间的请求 socket.on("client: joinRoom", (data) => { const { roomCode, userName } = data; const roomId = "room-" + roomCode; // 找到对应的房间 const room = rooms.find(r => r.room === roomCode); if (room) { socket.join(roomId); // 发送房间现有玩家列表给当前用户 socket.emit("server: roomPlayers", room.users); } }); }); export default server;
方案2:使用Socket.IO的查询参数传递房间信息
在初始化Socket时,通过查询参数携带房间编码,服务端自动让连接加入对应房间。
修改客户端大厅代码
跳转时存储房间编码,房间页初始化Socket时携带参数:
// 大厅代码不变,依然存储roomCode到sessionStorage const socket = io(); $("#createParty").click(() => { const code = newCode(); sessionStorage.setItem("roomCode", code); socket.emit("client: newParty", {userName: sessionStorage.getItem("userName"), code: code}); window.location.href = "/room"; }); $("#connectParty").click(() => { const codeConnect = $("#txt-codeConnect").val(); sessionStorage.setItem("roomCode", codeConnect); socket.emit("client: tryConnectParty", {userName: sessionStorage.getItem("userName"), codeConnect: codeConnect}); window.location.href = "/room"; }); export default socket;
修改客户端房间页代码
初始化Socket时携带房间编码参数:
// 不再导入大厅的socket,而是直接创建带参数的连接 const roomCode = sessionStorage.getItem("roomCode"); const socket = io({ query: { roomCode, userName: sessionStorage.getItem("userName") } }); const userName = sessionStorage.getItem("userName"); $("#txt-userName").html(`<b class='text-white'>${userName}</b>`); $("#txt-codeRoom").html(`<b>${roomCode}</b>`); var divPlayers = $("#players"); socket.on("server: newConnection", (userName) => { divPlayers.append(createCard(userName)); }); socket.on("server: roomPlayers", (players) => { divPlayers.empty(); players.forEach(player => { divPlayers.append(createCard(player)); }); }); function createCard(user) { return ` <div class="card d-inline-flex text-center" style="width: 300px;"> <div class="card-body"> <span class="h1 text-dark">${user}</span> </div> <img class="card-img-bottom" src="img/avatar.png" alt="avatar"> </div> `; }
修改服务端代码
在connection事件中自动处理查询参数,让连接加入房间:
// Import for server... import { Server as WebSocketServer } from "socket.io"; import http from "http"; import app from "./app.js"; const server = http.createServer(app); const io = new WebSocketServer(server); var rooms = []; export var usersRoom = []; // Events socket... io.on("connection", (socket) => { console.log("New conection:", socket.id); const { roomCode, userName } = socket.handshake.query; // 如果携带了房间编码,自动加入房间 if (roomCode && userName) { const roomId = "room-" + roomCode; const room = rooms.find(r => r.room === roomCode); if (room) { socket.join(roomId); socket.emit("server: roomPlayers", room.users); } } socket.on("client: newParty", (data) => { // data = {userName, code} const code = data.code; const roomId = "room-"+ code; socket.join(roomId); rooms.push({ room: code, users: [data.userName] }); }); socket.on("client: tryConnectParty", (data) => { // data = {userName, codeConnect} const code = data.codeConnect; const roomId = "room-"+ code; for (let i = 0; i < rooms.length; i++) { if (rooms[i].room == code) { const userName = data.userName; socket.join(roomId); rooms[i].users.push(userName); usersRoom = rooms[i].users; socket.to(roomId).emit("server: newConnection", userName); socket.emit("server: roomPlayers", rooms[i].users); break; } } }); }); export default server;
关键说明
页面跳转后浏览器会重新加载所有JS资源,原Socket连接会被断开,因此无法直接复用之前的连接。核心思路是在房间页重新建立连接后,主动让连接加入对应的房间,并同步房间内的玩家状态。
内容的提问来源于stack exchange,提问作者Faihd Enrique Pineda Duque
相关产品推荐
相关产品推荐

