WebSocket实战教程:构建多人联机游戏(第二部分)——路由与广播机制
websockets 项目地址: https://gitcode.com/gh_mirrors/web/websockets
前言
本教程是WebSocket实战系列的第二部分,我们将基于aaugustin/websockets库构建一个完整的多人联机四子棋游戏。在第一部分中,我们实现了单机版的游戏逻辑,现在我们要扩展它支持多玩家对战和观战功能。
游戏状态共享
在单进程服务器环境中,我们可以使用全局变量来共享游戏状态:
JOIN = {} # 全局字典,用于通过密钥查找游戏
游戏初始化流程
当第一个玩家创建游戏时,我们生成一个唯一的密钥并存储游戏实例:
import secrets
async def start(websocket):
game = Connect4()
connected = {websocket} # 连接集合
join_key = secrets.token_urlsafe(12) # 生成随机密钥
JOIN[join_key] = (game, connected) # 存储游戏状态
try:
# 发送初始化事件给第一个玩家
event = {"type": "init", "join": join_key}
await websocket.send(json.dumps(event))
# ...游戏逻辑...
finally:
del JOIN[join_key] # 游戏结束时清理
玩家加入流程
第二个玩家通过密钥加入已有游戏:
async def join(websocket, join_key):
try:
game, connected = JOIN[join_key] # 查找游戏
connected.add(websocket) # 添加新连接
try:
# ...游戏逻辑...
finally:
connected.remove(websocket) # 断开时清理
except KeyError:
await error(websocket, "Game not found.")
前端实现
初始化逻辑
前端需要根据URL参数决定发送哪种初始化事件:
function initGame(websocket) {
websocket.addEventListener("open", () => {
const params = new URLSearchParams(window.location.search);
let event = { type: "init" };
if (params.has("join")) {
event.join = params.get("join"); // 加入游戏
}
websocket.send(JSON.stringify(event));
});
}
分享链接生成
服务器返回的密钥用于生成分享链接:
// 处理初始化响应
case "init":
document.querySelector(".join").href = "?join=" + event.join;
break;
游戏逻辑实现
初始化完成后,游戏逻辑对双方玩家是对称的:
async def play(websocket, game, player, connected):
# 处理玩家移动
async for message in websocket:
event = json.loads(message)
# ...验证移动...
# 广播给所有连接
for connection in connected:
await connection.send(json.dumps(event))
观战功能扩展
要实现观战功能,我们需要:
- 使用独立的WATCH字典存储观战密钥
- 生成不同的观战密钥
- 添加观战处理函数
async def watch(websocket, watch_key):
try:
game, connected = WATCH[watch_key]
connected.add(websocket)
try:
await websocket.wait_closed() # 等待连接关闭
finally:
connected.remove(websocket)
except KeyError:
await error(websocket, "Game not found.")
高效广播机制
websockets库提供了高效的广播功能:
from websockets import broadcast
# 替代手动循环发送
broadcast(connected, json.dumps(event)) # 注意没有await
广播与单发的区别
- 单发(send):需要await,提供背压(backpressure)机制,防止发送过快
- 广播(broadcast):非协程,不等待慢客户端,适合大量连接场景
对于我们的游戏,两种方式实际效果相同,因为数据量很小。
关键知识点总结
- 连接路由:通过初始化消息决定处理逻辑
- 状态共享:在单进程中使用全局变量,多进程需要更复杂方案
- 连接管理:使用集合跟踪相关连接,注意资源清理
- 广播优化:理解背压机制,合理选择发送方式
注意事项
- 服务器重启会丢失所有游戏状态
- 测试时需要重新创建游戏并获取新链接
- 观战功能可以实现游戏回放
通过本教程,你已经掌握了WebSocket多人交互的核心模式,可以扩展到更复杂的实时应用场景。
websockets 项目地址: https://gitcode.com/gh_mirrors/web/websockets
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考