使用 python asyncio 从套接字读取时如何避免阻塞?

How to avoid blocking when reading from a socket with python asyncio?

我正在尝试使用 python asyncio 进行一些实验以在该领域进行改进,出于自学目的,我正在尝试连接到 redis,发送一些命令并读取响应,这可能会失败在通用 "read a stream of data from some source" 下。我无法解决的问题是如何以块的形式读取数据,因为服务器和客户端之间的连接没有关闭,终止序列 \r\n 可能不止一次被满足。如果我在没有更多数据时等待,当然调用将阻塞,直到收到其他数据。

class Client:
    def __init__(self, loop, host='127.0.0.1', port=6379):
        self.host = host
        self.port = port
        self.reader = None
        self.writer = None
        self.loop = loop

    @asyncio.coroutine
    def _connect(self):
        self.reader, self.writer = yield from asyncio.open_connection(
            self.host, self.port, loop=self.loop)

    async def read(self, b=4096):
        resp = b''
        while True:
            chunk = await self.reader.read(b)
            if chunk:
                resp += chunk
            else:
                break
        return resp

假设我想以 2 字节为单位读取响应(是的,这很愚蠢,但这只是为了这个学习目的)所以:

loop = asyncio.get_event_loop()
client = Client(loop)
..... sends some commands here ....
resp = await client.read(2)

我不知道在不知道服务器响应的长度的情况下,当响应长于从套接字读取的字节数时,代码仍然是安全的。

我最近遇到了类似的问题。我的解决方案是继续阅读,直到阅读给定字符(或字符集)为止。这与人们在通话结束后在对讲机上说 "over" 背后的理念相同。只等待响应说它已完成谈话会更容易。

虽然我之前没有使用过 asyncio 模块,但我相信以下代码应该可以解决您的问题,假设输入源以中指示的任何字符(或字符串)结束响应变量 end_signal.

class Client:
    def __init__(self, loop, host='127.0.0.1', port=6379):
        self.host = host
        self.port = port
        self.reader = None
        self.writer = None
        self.loop = loop

    @asyncio.coroutine
    def _connect(self):
        self.reader, self.writer = yield from asyncio.open_connection(
            self.host, self.port, loop=self.loop)

    async def read(self, b=4096, end_signal = "10101101110111110"):
        resp = b''
        while True:
            chunk = await self.reader.read(b)
            resp += chunk
            if resp[-1*len(end_signal):] == end_signal:
                break
        return resp