Websocket messages sent to multiple clients are not being received

I’m making a multiplayer game and I’m making the multiplayer its own package. In my server class, I have a broadcast method and a send_to_all_except method. The problem I’m having it that the messages are not being sent properly (the messages are not being received by the client(s)). Both methods are pretty much identical except for the client_websockets list so I’ll only put the code for broadcast here.

def broadcast(self, msg):
    client_websockets = [client.ws for client in self.clients]
    for ws in client_websockets:
        asyncio.create_task(self._send(ws, msg))

I know that client_websockets is not empty when running it so that’s not the problem. This is how it is done in the websockets documentation.

The code for _send is very simple and is shown below.

async def _send(self, ws, msg):
    try:
        await ws.send(msg)
    except websockets.ConnectionClosed:
        self.clients.remove([client for client in self.clients if client.ws == ws])

I have tried using asyncio.create_task() as in the code above and asyncio.ensure_future() but neither work.

All code is on GitHub.

I asked this exact question on Stack Overflow, but I got no responses and a downvote. My questions always seem to be frowned upon on there. Any feedback on why that might be would be appreciated.

Thanks!

Because StackOverflow. The site’s pretty awful for asking questions on.

This looks a bit odd though:

self.clients.remove([client for client in self.clients if client.ws == ws])

I wonder if it’d be easier to just pass the client object, rather than the socket.

async def _send(self, cli, msg):
    try:
        await cli.ws.send(msg)
    except websockets.ConnectionClosed:
        self.clients.remove(client)

def broadcast(self, msg):
    for cli in self.clients:
        asyncio.create_task(self._send(cli, msg))

But that might break other things in your code - I haven’t looked.

Refer to: How to create a Minimal, Reproducible Example - Help Center - Stack Overflow

1 Like

Sorry for late reply. Have been busy.
Thanks for the simplification. I will write a reproducible example soon so hopefully you could see what the problem is then.

1 Like

Again, sorry for the delay but I have some new info.

While writing the example, I’ve discovered that the issue isn’t actually with the server, which I’ve thought this whole time. It is rather with how the client handles messages. I have the main logic in the main thread, and the message handler in a side thread. This is causing issues somewhere. Here’s the example:

Server:

import websockets
import asyncio

clients = []


async def broadcast(msg):
    print(f"Broadcasting: {msg}")
    for ws in clients:
        await ws.send(msg)


async def proxy(websocket):
    clients.append(websocket)
    async for msg in websocket:
        print(msg)
        await broadcast(msg)


async def run():
    async with websockets.serve(proxy, "127.0.0.1", 6000):
        await asyncio.Future()

if __name__ == "__main__":
    asyncio.run(run())

Client:

import websockets
import asyncio
from threading import Thread


async def proxy(websocket):
    while True:
        msg_to_send = input("Message: ")
        await websocket.send(msg_to_send)


async def run():
    async with websockets.connect("ws://127.0.0.1:6000") as websocket:
        thread = Thread(target=start_websocket_thread, args=(websocket,))
        await proxy(websocket)


async def websocket_handler(websocket):
    async for msg in websocket:
        print(f"Message Received: {msg}")


def start_websocket_thread(websocket):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    loop.run_until_complete(websocket_handler(websocket))


if __name__ == "__main__":
    asyncio.run(run())

Note:
The websocket_handler function in the client has to be asynchronous.

If you want any other information, just let me know and I’ll try not to take 5-7 business days :wink:

No but seriously thanks for helping me with such inconsistent replies.

1 Like

Okay, yeah, this is definitely a problem. Spawning a thread is fast, and then you close out the async with block, which will close the websocket. Is there a reason you can’t simply await websocket_handler(websocket) ? That would be the most normal and logical way to do it.

I’m using a thread because this package is for multiplayer games and I need the game loop running constantly (the game loop is in the proxy function). Now that I think about it, I could probably just have some code in the game loop to check for messages every frame. I’m not on my computer right now so I’ll check it out once I am. I got this thread solution from @elis.byberi in this post.

Yes, but rather than creating an event loop directly inside a thread, you set up an event loop that runs a coroutine, which then spawns a thread to create another event loop.