How to use Python websockets and asyncio to send data periodically and wait for receiving data but without blocking the sending process
Asked Answered
O

2

5

I've Been searching for quite a long time now and practicing a bit but I can't find a solution.

Here's my goal:

  • I would like a Javascript Client that occasionally sends data to my server

  • I'd like a server (in Python) that periodically (every second or so) sends data to my client.

Here's what I've got:
I'm able to send data to my client and process it as I want.
I'm able to send data to my server but I'm not able to receive it in my Server. It has to be a non-blocking process and because the data is occasionally sent, I'm having trouble implementing this in my server code.

My server code so far is:

import asyncio
import random
import websockets
import json


async def recv(websocket, path):
    try:
        name = asyncio.wait_for(websocket.recv(), timeout=2)
    except TimeoutError:
        print("Time exceeded")
        name = None
    await asyncio.sleep(0.5)


async def send(websocket, path):
    data = [
            {
              "name": "Random Int 1",
              "number": random.randint(0, 1000)
            },
            {
              "name": "Random Int 2",
              "number": random.randint(1001, 2000)
            },
            {
              "name": "Random Int 3",
              "number": random.randint(2001, 3000)
            }
    ]
    await websocket.send(json.dumps(data))
    await asyncio.sleep(0.5)


async def main(websocket, path):
    while True:
        send_task = asyncio.create_task(send(websocket, path))
        recv_task = asyncio.create_task(recv(websocket, path))
        await send_task
        await recv_task

start_server = websockets.serve(main, "localhost", 3500)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

my errors are:

program.py:43: DeprecationWarning: There is no current event loop
  asyncio.get_event_loop().run_until_complete(start_server)
program.py:44: DeprecationWarning: There is no current event loop
  asyncio.get_event_loop().run_forever()
python3.10/asyncio/events.py:80: RuntimeWarning: coroutine 'wait_for' was never awaited
  self._context.run(self._callback, *self._args)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
python3.10/asyncio/events.py:80: RuntimeWarning: coroutine 'WebSocketCommonProtocol.recv' was never awaited
  self._context.run(self._callback, *self._args)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

Any tips ? Thanks a lot !

Orchidectomy answered 21/6, 2022 at 9:39 Comment(0)
P
5

Try this server code:

import asyncio
import random
import websockets
import json


async def handler(websocket):

    # create periodic task:
    asyncio.create_task(send(websocket))

    while True:
        try:
            message = await websocket.recv()
            print(message)

        # client disconnected?
        except websockets.ConnectionClosedOK:
            break


async def send(websocket):
    while True:
        data = [
            {"name": "Random Int 1", "number": random.randint(0, 1000)},
            {"name": "Random Int 2", "number": random.randint(1001, 2000)},
            {"name": "Random Int 3", "number": random.randint(2001, 3000)},
        ]

        try:
            await websocket.send(json.dumps(data))

        # client disconnected?
        except websockets.ConnectionClosedOK:
            break

        await asyncio.sleep(0.5)


async def main():
    async with websockets.serve(handler, "localhost", 3500):
        await asyncio.Future()  # run forever


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

How to test it?

  • in one terminal run this server code
  • in other terminal run this python code: python -m websockets ws://localhost:3500

You will immediately see that the server sends every 0.5 seconds some random Json messages. You can type some message and press enter and see that the message is printed in the server screen. To exit client press Ctrl+D

Palmer answered 22/6, 2022 at 0:14 Comment(0)
O
2

It seems that I found a working solution but not sure that this is the best...

Here's my code:

import asyncio
import random
import websockets
import json


async def recv(websocket, path):
    try:
        name = await asyncio.wait_for(websocket.recv(), timeout=0.1) #I forgot the "await" \facepalm
        print("name", name)
    except asyncio.TimeoutError:
        print("No Data")
    await asyncio.sleep(0.1)

async def send(websocket, path):
    data = [
            {
              "name": "Random Int 1",
              "number": random.randint(0, 1000)
            },
            {
              "name": "Random Int 2",
              "number": random.randint(1001, 2000)
            },
            {
              "name": "Random Int 3",
              "number": random.randint(2001, 3000)
            }
    ]
    await websocket.send(json.dumps(data))
    await asyncio.sleep(0.1)


async def main2(websocket, path):
    while True:
        send_task = asyncio.create_task(send(websocket, path))
        await send_task
        recv_task = asyncio.create_task(recv(websocket, path))
        await recv_task

#rewrite of this part is to remove Deprecation Warning
async def main():
    server = await websockets.serve(main2, 'localhost', 3500)
    await server.wait_closed()

asyncio.run(main())
Orchidectomy answered 21/6, 2022 at 14:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.