1

I'm currently trying to integrate a websocket interface to my program. I'm using the https://websockets.readthedocs.io/en/stable/intro.html module for this purpose, along with asyncio. I'm currently struggling to implement the websocket functionalites in a dedicated class that sets up the websocket tasks to run in concurrency in the same event loop as the task running in MyDriver class.

main.py

from myDriver import MyDriver
from webSocketServer import WebSocketServer

async def main():

   # loop = asyncio.get_event_loop()
   driver = MyDriver()
   ws = WebSocketServer()
   await driver.drive()
   # The following does not integrate properly with the above. The msgHandler is not ran 
   await websockets.serve(lambda websocket, path: ws.msgHandler(websocket, path), "localhost", 5678)

asyncio.run(main())

The lambda is here to get rid of the self argument coming from the class.

webSocketServer.py

import asyncio
import websockets

class WebSocketServer:

    def __init__(self):
        print('Init')

    async def msgHandler(self, websocket, path):
        self.sendTask = asyncio.create_task(self.sendHandler(websocket, path))
        self.receiveTask = asyncio.create_task(self.receiveHandler(websocket, path))

        await asyncio.wait([self.sendTask, self.receiveTask], return_when=asyncio.FIRST_COMPLETED)

    async def sendHandler(self, websocket, path):
        while True:
            await asyncio.sleep(2)
            message = producer()
            await websocket.send(message)

    async def receiveHandler(self, websocket, path):
        async for message in websocket:
            await self.printMsg()

    async def printMsg(self, msg):
        await asyncio.sleep(0.1)
        print(msg)

    def producer():
        return 'Hi !'

I based my implementation on the examples provided on the websockets' Getting Started page. They use the loop.run_until_complete(server) & loop.run_forever() APIs. I also tried to use these by passing the loop in argument to the constructor of WebSocketServer(loop) and do the websockets.serve(lambda websocket, path: ws.msgHandler(websocket, path), "localhost", 5678) there, but then I get the error RuntimeError: This event loop is already running. I also had a look at loop.create_task(), which takes a coroutine as an argument.

Does anyone see a way I could properly integrate a websocket server running in the same event loop as my other task? Thx !

1 Answer 1

3

They use the loop.run_until_complete(server) & loop.run_forever() APIs.

It is good that you converted those to asyncio.run(), which is a much more robust way to start up asyncio code. It's just that your conversion is incomplete, it's missing the run_forever() part. Something like run_forever() is needed because websockets.serve() doesn't mean "serve and exit when done", it means "start serving and return a handle to the server". This handle is returned (almost) immediately and if you return from main at that point, as your code does, the program will just exit before it has handled a single connection.

What you need to do is add another await at the end of main, where you await for as long as the program needs to run. One way is to await asyncio.Event().wait(), which will wait forever and is the await equivalent of loop.run_forever(). And since you have the server object at your disposal, you can use its wait_closed() method to wait for as long as the server is running (i.e. a coroutine intentionally closes it by calling server.close()), which is probably what you want.

In other words, you need to modify the last line of main() to:

   server = await websockets.serve(lambda websocket, path: ws.msgHandler(websocket, path), "localhost", 5678)
   await server.wait_closed()
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.