konze
konze

Reputation: 893

Closing/Shutting down a Python Websocket

I extended the websocket echo server example (here) such that it is embedded into a Tkinter application. The server will be started when the "Start" button is pressed. Now I would like to stop the server when pressing the "Stop" button. However, close() does not work.

server.py:

try:
    import Tkinter as Tk
    from Tkinter import ttk
except ModuleNotFoundError:
    import tkinter as Tk
    from tkinter import ttk

import asyncio
import websockets


class TkEcho():
    def __init__(self):
        asyncio.get_event_loop().run_until_complete(self.main())


    async def main(self):
        '''
        Add Buttons
        '''
        root = Tk.Tk()

        Tk.Button(root, text='Start', command=lambda: self.start("localhost", 8765)).grid()
        Tk.Button(root, text='Stop', command=self.stop).grid()

        await self.run_tk(root)


    async def run_tk(self, root, interval=0.05):
        '''
        Add Tk to asyncio
        '''
        try:
            while True:
                root.update()
                await asyncio.sleep(interval)
        except Tk.TclError as e:
            if "application has been destroyed" not in e.args[0]:
                raise


    async def echo(self, websocket, path):
        '''
        Receives string and sends it back to sender
        '''
        string = await websocket.recv()
        print(f"recv < {string}")
        await websocket.send(string)
        print(f"send > {string}")


    def start(self, host, port):
        '''
        Starts websocket
        '''
        start_server = websockets.serve(self.echo, host, port)
        asyncio.ensure_future(start_server)


    def stop(self):
        '''
        Stops websocket
        '''
        None


if __name__ == "__main__":
    t = TkEcho()

client.py:

import asyncio
import websockets

    async def send_recv():
    uri = "ws://localhost:8765"
    async with websockets.connect(uri) as websocket:
        string = input("string to send? ")

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

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

asyncio.get_event_loop().run_until_complete(send_recv())

Solution:

replace TkEcho.__init__ with:

def __init__(self):
    loop = asyncio.get_event_loop()
    loop.create_task(self.main())
    loop.run_forever()

And implement TkEcho.stop as follows:

def stop(self):
    '''
    Stops websocket
    '''
    asyncio.get_event_loop().stop()

Upvotes: 0

Views: 5669

Answers (1)

user4815162342
user4815162342

Reputation: 154836

To stop the program, you can simply stop the event loop:

    def stop(self):
        # terminate the loop
        asyncio.get_event_loop().stop()

For this to work, you will need to use run_forever() instead of run_until_complete() to run the event loop:

    def __init__(self):
        loop = asyncio.get_event_loop()
        loop.create_task(self.main())
        loop.run_forever()  # wait for loop.stop() to terminate us

Upvotes: 2

Related Questions