使用 websocket 的 Python 多人游戏:处理多个客户端

phy*_*cus 7 python asynchronous multiplayer websocket async-await

我正在尝试通过websockets在 Python 中构建多人游戏(准确地说是纸牌游戏),但目前我在早期步骤中失败了。

我正在尝试做的事情:

  • 客户端连接到 websocket,直到达到一定数量
  • 客户端向服务器发送输入
  • 服务器分别并同时响应每个客户端

什么有效:

  • 让客户端连接并为每个客户端存储一个 websocket 按预期工作,但我有点卡住了。

我尝试过的:

客户端.py

import asyncio
import websockets
import random


def random_name():
    return "".join([random.choice("abcdefghijkl") for _ in range(5)])


async def main():
    uri = "ws://localhost:1235"
    print("Starting...")
    async with websockets.connect(uri) as ws:
        client_name = random_name()
        await ws.send(client_name)
        print(await ws.recv())  # server sends :client_name registered
        print(await ws.recv())  # server sends: 3 people registered
        print(await ws.recv())  # server sends: Waiting for input
        inp = input()
        await ws.send(inp)

asyncio.get_event_loop().run_until_complete(main())
Run Code Online (Sandbox Code Playgroud)

server.py (非常幼稚的方法)

import websockets
import asyncio

clients = []


async def main(ws, *args, **kwargs):
    while True:
        print(f"Waiting for new connection! {ws}")
        client_name = await ws.recv()
        clients.append((client_name, ws, ws.remote_address))
        await ws.send(f"{client_name} registered on {ws.remote_address}")

        if len(clients) >= 3:
            registered_clients = ', '.join([c[0] for c in clients])
            print(f"3 clients ({registered_clients}) registered")
            break

    for name, ws_, _ in clients:
        await ws_.send(f"3 clients registered ({registered_clients})")
        await ws_.send(f"Waiting for input")

    for _ in clients:
        inp = await ws_.recv()
        print(ws_.remote_address, inp)


start_server = websockets.serve(main, "localhost", "1235")

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

Run Code Online (Sandbox Code Playgroud)

这失败了RuntimeError: cannot call recv while another coroutine is already waiting for the next message。看来我不能单独等待每个连接的客户端的答复。尽管并不完美,但我认为我可以通过查看远程地址来区分每个客户端。为此,我将 main 函数修改为

server.py,主要功能(简单方法)

async def main(ws, *args, **kwargs):
    while True:
        print(f"Waiting for new connection! {ws}")
        client_name = await ws.recv()
        clients.append((client_name, ws, ws.remote_address))
        await ws.send(f"{client_name} registered on {ws.remote_address}")

        if len(clients) >= 3:
            registered_clients = ', '.join([c[0] for c in clients])
            print(f"3 clients ({registered_clients}) registered")
            break

    for name, ws_ in clients:
        await ws_.send(f"3 clients registered ({registered_clients})")
        await ws_.send(f"Waiting for input")

    for _ in range(len(clients)):
        inp = await ws.recv()
        print(ws.remote_address, inp)
Run Code Online (Sandbox Code Playgroud)

这也不起作用,因为循环不会中断所有连接的客户端。

我的问题:

  • 我的例子如何修复?
  • 该软件包是websockets适合我的框架吗?有两件事让我想到:
    • 对于套接字,我可以将一个客户端绑定到套接字实例并直接从一个实例读取(我在这里似乎无法轻松做到这一点)。
    • 尽管我很确定,可以实现我想要的目标,但似乎需要很多代码来实现这一点(!?)
  • 但不知何故,我想总的来说坚持使用网络套接字(因为也有可能连接到浏览器进行通信)。特别是坚持使用这个websockets包,将有利于我更好地了解该asyncio模块。但如果有人说,我应该切换到aiohttp,等等flask-socketstornado我也愿意这样做。