I'd like to encapsulate the functionality of the python websockets package into a class, representing a sensor coordinator. The aim of this is to allow me to create a coordinator object, and only have the server persist for as long as it is needed. Unfortunately, I have not been able to find any similar examples of this online and have so far struggled.
My code is as follows:
import asyncio
import json
import logging
import websockets
logging.basicConfig()
class Coordinator(object):
def __init__(self, host='localhost', port=8080):
self.host = host
self.port = port
self.running = False
self.server = None
self.sensors = set()
def __enter__(self):
self.server = websockets.serve((self.ws_handler, self.host, self.port))
self.running = True
def __exit__(self, exc_type, exc_val, exc_tb):
# Gracefully stop serving
self.running = False
pass
def sensors_event(self):
return json.dumps({'type': 'sensors', 'count': len(self.sensors)})
async def notify_sensors(self):
if self.sensors:
message = self.sensors_event()
await asyncio.wait([user.send(message) for user in self.sensors])
async def register(self, websocket):
self.sensors.add(websocket)
await self.notify_sensors()
async def unregister(self, websocket):
self.sensors.remove(websocket)
await self.notify_sensors()
async def ws_handler(self, websocket):
try:
await self.register(websocket)
pass
finally:
await self.unregister(websocket)
if __name__ == '__main__':
with Coordinator() as coordinator:
pass
At the moment it would appear that the websocket server does not start, as it is not visible on netstat.
Would it be possible to run the server in a separate (demonised) thread, held by the coordinator object?
Thanks
From the high-level documentation :
The
websockets.server
module defines a simpleWebSocket
server API.
serve()
returns an awaitable. Awaiting it yields an instance ofWebSocketServer
which providesclose()
andwait_closed()
methods for terminating the server and cleaning up its resources.On Python ≥ 3.5,
serve()
can also be used as an asynchronous context manager. In this case, the server is shut down when exiting the context.
As @user4815162342 already identified, the main issue is that you do not await the call to the serve()
coroutine.
Since you're using Python v3.6.8 you can use the asynchronous context manager to simplify the implementation. The benefit of this is that you do not need to worry about handling shutdown, since it is handled automatically. Here's an object oriented implementation of a simple echo server.
import asyncio
import signal
import websockets
class Server(object):
def __init__(self, host, port):
self.host, self.port = host, port
self.loop = asyncio.get_event_loop()
self.stop = self.loop.create_future()
self.loop.add_signal_handler(signal.SIGINT, self.stop.set_result, None)
self.loop.run_until_complete(self.server())
async def server(self):
async with websockets.serve(self.ws_handler, self.host, self.port):
await self.stop
async def ws_handler(self, websocket, path):
msg = await websocket.recv()
print(f'Received: {msg}')
await websocket.send(msg)
print(f'Sending: {msg}')
if __name__ == '__main__':
server = Server(host='localhost', port=6789)
At the moment, this will run until the user sends an interrupt, but you can adjust the stop
future to suit.
Your code has two issues.
You never start the asyncio main loop, so asyncio has no chance of ever running. In other words, you need to have loop.run_until_complete(x)
somewhere in your code.
start_server
is a coroutine, so you must await it.
A fixed version of the code (but untested) might look like this:
class Coordinator(object):
def __init__(self, host='localhost', port=8080):
self.host = host
self.port = port
self.running = False
self.server = None
self.sensors = set()
async def __aenter__(self):
self.server = await websockets.serve((self.ws_handler, self.host, self.port))
self.running = True
def __aexit__(self, exc_type, exc_val, exc_tb):
# Gracefully stop serving
self.running = False
def sensors_event(self):
return json.dumps({'type': 'sensors', 'count': len(self.sensors)})
async def notify_sensors(self):
if self.sensors:
message = self.sensors_event()
await asyncio.wait([user.send(message) for user in self.sensors])
async def register(self, websocket):
self.sensors.add(websocket)
await self.notify_sensors()
async def unregister(self, websocket):
self.sensors.remove(websocket)
await self.notify_sensors()
async def ws_handler(self, websocket):
try:
await self.register(websocket)
finally:
await self.unregister(websocket)
async def main():
async with Coordinator() as coordinator:
pass
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(main())
It will be much easier to use asyncio if you take the time to go through a tutorial that covers basic asyncio concepts, such as running the main loop.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.