ООП Python веб-сокеты - PullRequest
       16

ООП Python веб-сокеты

0 голосов
/ 31 января 2019

Я хотел бы инкапсулировать функциональность пакета python websockets в класс, представляющий координатор датчика.Цель этого состоит в том, чтобы позволить мне создать объект координатора, и только сервер должен сохраняться столько, сколько это необходимо.К сожалению, я не смог найти подобных примеров в Интернете и до сих пор боролся.

Мой код выглядит следующим образом:

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

В настоящий момент может показаться, что сервер веб-сокетов не запускается, поскольку он не отображается в netstat.

Будетможно ли запустить сервер в отдельном (демонизированном) потоке, удерживаемом объектом-координатором?

Спасибо

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Из документации высокого уровня :

Модуль websockets.server определяет простой WebSocket серверный API.

serve() возвращает ожидаемое.Ожидание этого приводит к экземпляру WebSocketServer, который предоставляет close() и wait_closed() методы для завершения работы сервера и очистки его ресурсов.

На Python ≥ 3.5, serve() также может использоваться как асинхронныйконтекстный менеджер.В этом случае сервер закрывается при выходе из контекста.

Поскольку @ user4815162342 уже определен, основная проблема заключается в том, что вы не ожидаете вызова к сопрограмме serve().

Поскольку вы используете Python v3.6.8, вы можете использовать асинхронный менеджер контекста, чтобы упростить реализацию.Преимущество этого заключается в том, что вам не нужно беспокоиться об обработке завершения работы, поскольку она обрабатывается автоматически.Вот объектно-ориентированная реализация простого эхо-сервера.

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)

В данный момент это будет выполняться до тех пор, пока пользователь не отправит прерывание, но вы можете настроить stop future, чтобы удовлетворить.

0 голосов
/ 01 февраля 2019

Ваш код имеет две проблемы.

  • Вы никогда не запустите основной цикл asyncio, поэтому у asyncio нет никаких шансов на его запуск.Другими словами, вам нужно иметь loop.run_until_complete(x) где-то в вашем коде.

  • start_server - это сопрограмма, поэтому вы должны его дождаться.

Исправленная версия кода (но не проверенная) может выглядеть следующим образом:

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())

Будет намного проще использовать asyncio, если вы потратите время на просмотр учебника охватывает основные понятия асинхронности, такие как запуск основного цикла.

...