Python websockets lib客户端持久连接(带有类实现)

时间:2019-12-04 18:39:54

标签: python websocket python-asyncio

我正在尝试使用websockets和我以前从未使用过的强制性asyncio在python中实现一个websocket客户端(并且我很难理解...)。

我已经阅读了很多有关该主题的文章,并且在各处和任何地方都看到(太多)示例,但是我找不到一种方法来正确地建立具有持久连接的websocket客户端。

  • 我需要建立持久连接,因为需要在同一连接上请求命令,第一个是身份验证命令。
  • 远程服务器是第三方API,我无法控制。
  • 我想我可以与程序发送的每个命令一起运行一个身份验证请求,但是对于每个命令来说,打开一个> auth> request> close命令感到不合适,而不是在整个程序的生命周期中保持一个连接处于活动状态
  • 我的实现是一个使用许多类的库,我需要将websocket连接器/处理程序包装在其中一个中

根据我在这里到处发现的示例(带有一些混淆的数据),这就是我现在拥有的:

import json
import asyncio
from websockets import connect


URL = 'wss://server.com/endpoint'


class Websocket:
    async def __aenter__(self):
        self._conn = connect(URL)
        self.websocket = await self._conn.__aenter__()
        return self

    async def __aexit__(self, *args, **kwargs):
        await self._conn.__aexit__(*args, **kwargs)

    async def send(self, message):
        await self.websocket.send(message)

    async def receive(self):
        return await self.websocket.recv()


class Handler:
    def __init__(self):
        self.wws = Websocket()
        self.loop = asyncio.get_event_loop()

    def command(self, cmd):
        return self.loop.run_until_complete(self.__async__command(cmd))

    async def __async__command(self, cmd):
        async with self.wws as echo:
            await echo.send(json.dumps(cmd))
            return await echo.receive()


def main():
    handler = Handler()

    foo = handler.command('authentication command')
    print('auth: ', foo)

    bar = handler.command('another command to run depending on the first authentication')
    print('command: ', bar)


if __name__ == '__main__':
    main()

基本上现在我得到了这些答案(简化和混淆):

auth: Ok, authenticated
command: Command refused, not authenticated

我想我的问题是,块async with self.wws as echo:会创建连接,先运行其代码然后将其删除,而不是保持连接处于活动状态。由于我们在这里使用的不是常用的__init__,而是一些我不理解的asyncio伏都教徒,所以我有些困惑。

1 个答案:

答案 0 :(得分:1)

我认为您的诊断是正确的,问题在于它为每个Handler.command的调用创建并关闭连接的异步上下文管理器...确实不想要您想要的。

相反,您可以在Handler初始化期间同步建立websocket连接,然后将连接websocket(类型为WebSocketClientProtocol的实例)存储为类成员,以供以后使用,如此示例代码中所示:

import json
import asyncio
from websockets import connect

URL = 'ws://localhost:8000'

class Handler:

    def __init__(self):
        self.ws = None
        self.loop = asyncio.get_event_loop()
        # perform a synchronous connect
        self.loop.run_until_complete(self.__async__connect())

    async def __async__connect(self):
        print("attempting connection to {}".format(URL))
        # perform async connect, and store the connected WebSocketClientProtocol
        # object, for later reuse for send & recv
        self.ws = await connect(URL)
        print("connected")

    def command(self, cmd):
        return self.loop.run_until_complete(self.__async__command(cmd))

    async def __async__command(self, cmd):
        await self.ws.send(json.dumps(cmd))
        return await self.ws.recv()


def main():
    handler = Handler()

    foo = handler.command('authentication command')
    print('auth: ', foo)

    bar = handler.command('another command to run depending on the first authentication')
    print('command: ', bar)


if __name__ == '__main__':
    main()