How to stop websocket server created with websockets.serve()?

Viewed 4289

I have websocket serwer created with this package https://websockets.readthedocs.io/en/stable/intro.html. I must stop restart this server at runtime.

My server runs as asyncio.Task but cancel() method seems to do nothing. Is there any way to stop this server task and after that start it again?

server.py

#!/usr/bin/env python3

import asyncio
import websockets


class App:
    def __init__(self):
        self._task = None
        self._slots = []

    async def worker(self, websocket, path):
        data = await websocket.recv()
        self._slots.append(data)
        await websocket.send(f'prev: {"; ".join(self._slots)}')

        self.stop_server()

    def start_server(self):
        self._task = asyncio.ensure_future(websockets.serve(self.worker, 'localhost', 5000))

    def stop_server(self):
        self._task.cancel() # this doeas not stop server :P
        print(self._task.cancelled()) # always False

    def run(self):
        try:
            loop = asyncio.get_event_loop()
            self.start_server()
            loop.run_forever()
        finally:
            loop.close()

App().run()

Test client

#!/usr/bin/env python

# WS client example

import asyncio
import websockets

async def hello():
    async with websockets.connect(
            'ws://localhost:5000') as websocket:
        name = input("What's your name? ")

        await websocket.send(name)
        print(f"> {name}")

        greeting = await websocket.recv()
        print(f"< {greeting}")

asyncio.get_event_loop().run_until_complete(hello())
1 Answers

Under the Deployment section in the link you gave, there's a Graceful Shutdown section: https://websockets.readthedocs.io/en/stable/deployment.html#graceful-shutdown

You may want to close connections gracefully when shutting down the server, perhaps after executing some cleanup logic. There are two ways to achieve this with the object returned by serve():

  • using it as a asynchronous context manager, or
  • calling its close() method, then waiting for its wait_closed() method to complete.

On Unix systems, shutdown is usually triggered by sending a signal.

Here’s a full example (Unix-only):

#!/usr/bin/env python

import asyncio
import signal
import websockets

async def echo(websocket, path):
    async for message in websocket:
        await websocket.send(message)

async def echo_server(stop):
    async with websockets.serve(echo, "localhost", 8765):
        await stop

loop = asyncio.get_event_loop()

# The stop condition is set when receiving SIGTERM.
stop = loop.create_future()
loop.add_signal_handler(signal.SIGTERM, stop.set_result, None)

# Run the server until the stop condition is met.
loop.run_until_complete(echo_server(stop))

Note that the above is a Unix example only.

I got the same thing working on Windows by replacing the stop awaitable with the following:

import threading
stop_event = threading.Event()
stop = asyncio.get_event_loop().run_in_executor(None, stop_event.wait)

Then just call stop_event.set() when you want to stop the server

Related