简体   繁体   English

OOP Python websockets

[英]OOP Python websockets

I'd like to encapsulate the functionality of the python websockets package into a class, representing a sensor coordinator.我想将 python websockets包的功能封装到一个类中,代表一个传感器协调器。 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.目前看来 websocket 服务器没有启动,因为它在 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 simple WebSocket server API. websockets.server模块定义了一个简单的WebSocket服务器 API。

serve() returns an awaitable. serve()返回一个可等待的。 Awaiting it yields an instance of WebSocketServer which provides close() and wait_closed() methods for terminating the server and cleaning up its resources.等待它会产生一个WebSocketServer实例,它提供close()wait_closed()方法来终止服务器并清理其资源。

On Python ≥ 3.5, serve() can also be used as an asynchronous context manager.在 Python ≥ 3.5 上, serve()也可以用作异步上下文管理器。 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.正如@user4815162342 已经确定的那样,主要问题是您没有等待对serve()协程的调用。

Since you're using Python v3.6.8 you can use the asynchronous context manager to simplify the implementation.由于您使用的是 Python v3.6.8,您可以使用异步上下文管理器来简化实现。 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.目前,这将一直运行直到用户发送中断,但您可以调整stop未来以适应。

Your code has two issues.您的代码有两个问题。

  • You never start the asyncio main loop, so asyncio has no chance of ever running.你永远不会启动 asyncio 主循环,所以 asyncio 没有机会运行。 In other words, you need to have loop.run_until_complete(x) somewhere in your code.换句话说,您需要在代码中的某处使用loop.run_until_complete(x)

  • start_server is a coroutine, so you must await it. start_server是一个协程,所以你必须等待它。

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.如果您花时间阅读涵盖基本 asyncio 概念(例如运行主循环)的教程,则使用 asyncio 会容易得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM