简体   繁体   English

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

[英]Python websockets lib client persistent connection (with class implementation)

I'm trying to implement a websocket client in python using websockets and the apparently mandatory asyncio which I never used before (and I have a hard time to understand...).我正在尝试使用websockets和我以前从未使用过的明显强制性的asyncio在 python 中实现一个 websocket 客户端(我很难理解......)。

I've read a lot on the subject and saw (too) many examples here and everywhere, but I can't find a way to properly make a websocket client with a persistent connection.我已经阅读了很多关于这个主题的文章,并且在这里和到处都看到了(太多)很多例子,但是我找不到一种方法来正确地制作一个具有持久连接的 websocket 客户端。

  • I need to have a persistent connection because the commands need to be requested on the same connection, the first one being an authentication command.我需要有一个持久连接,因为需要在同一个连接上请求命令,第一个是身份验证命令。
  • The remote server is a 3rd party API I don't have any control over.远程服务器是我无法控制的第 3 方 API。
  • I suppose I could run an authentication request along with each command my program sends but that does not feel right to open > auth > request > close for each command instead of keeping one connection alive during the whole program's life我想我可以在我的程序发送的每个命令的同时运行一个身份验证请求,但是对于每个命令打开 > auth > request > close 而不是在整个程序的生命周期中保持一个连接处于活动状态感觉不对
  • My implementation is a library using many classes and I need to wrap the websocket connector/handler in one of them我的实现是一个使用许多类的库,我需要将 websocket 连接器/处理程序包装在其中一个中

Here's what I have right now, based on examples I found here and there (with some obfuscated data) :这是我现在所拥有的,基于我在这里和那里找到的示例(带有一些混淆数据):

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

Basically right now I get these answers (simplified and obfuscated) :基本上现在我得到这些答案(简化和混淆):

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

I suppose my problem is that the block async with self.wws as echo: kind of create the connection, runs its code then drop it instead of keeping the connection alive.我想我的问题是该块async with self.wws as echo:创建连接,运行其代码然后删除它而不是保持连接处于活动状态。 Since we are not using a usual __init__ here but some asyncio voodoo I don't understand, I'm kind of stuck.由于我们在这里没有使用通常的__init__而是一些我不明白的asyncio voodoo,我有点卡住了。

I think your diagnosis is correct, the problem is that the async context manager it creating and closing a connection for each call of Handler.command ... really not want you want.我认为您的诊断是正确的,问题是异步上下文管理器为每次Handler.command调用创建和关闭连接......真的不想要你想要的。

Instead you could just synchronously establish the websocket connection during the init of Handler and then store the connection websocket (instance of type WebSocketClientProtocol ) as a class member for later use, as in this sample code:相反,您可以在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()

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

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