WebSocket实战教程:构建多人联机游戏(第二部分)——路由与广播机制

WebSocket实战教程:构建多人联机游戏(第二部分)——路由与广播机制

websockets 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))

观战功能扩展

要实现观战功能,我们需要:

  1. 使用独立的WATCH字典存储观战密钥
  2. 生成不同的观战密钥
  3. 添加观战处理函数
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

广播与单发的区别

  1. 单发(send):需要await,提供背压(backpressure)机制,防止发送过快
  2. 广播(broadcast):非协程,不等待慢客户端,适合大量连接场景

对于我们的游戏,两种方式实际效果相同,因为数据量很小。

关键知识点总结

  1. 连接路由:通过初始化消息决定处理逻辑
  2. 状态共享:在单进程中使用全局变量,多进程需要更复杂方案
  3. 连接管理:使用集合跟踪相关连接,注意资源清理
  4. 广播优化:理解背压机制,合理选择发送方式

注意事项

  1. 服务器重启会丢失所有游戏状态
  2. 测试时需要重新创建游戏并获取新链接
  3. 观战功能可以实现游戏回放

通过本教程,你已经掌握了WebSocket多人交互的核心模式,可以扩展到更复杂的实时应用场景。

websockets websockets 项目地址: https://gitcode.com/gh_mirrors/web/websockets

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

段琳惟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值