了解python asyncio协议

时间:2018-06-28 09:21:02

标签: python python-asyncio

我对Python异步和协议的工作方式缺乏了解。

似乎相关的文献:
class asyncio.Protocol
Protocol.eof_received()状态机显示相关转换 AbstractEventLoop.run_until_complete(future)

示例代码为echo tcp client:

import asyncio

class EchoClientProtocol(asyncio.Protocol):
    def __init__(self, message, loop):
        self.message = message.encode()

    def connection_made(self, transport):
        self.transport = transport
        self.write_data()

    def data_received(self, data):
        print('Data received: {!r}',len(data))
        self.write_data()

    def eof_received(self):
        print("eof")
        return True

    def write_data(self):
        print("write")
        self.transport.write(self.message)

    def connection_lost(self, exc):
        print('The server closed the connection')
        print('Stop the event loop')


loop = asyncio.get_event_loop()
message = 'Hello World!'

coro = loop.create_connection(lambda: EchoClientProtocol(message, loop),
                              '127.0.0.1', 5676)
loop.run_until_complete(coro)
print("done")

在回显服务器上连接时的输出:

write
Data received: {!r} 12
write
done

据我了解,这应该一直运行到关闭连接。

发件人:Connection callbacks

每个成功连接都会精确地调用一次

connection_made()和connection_lost()。所有其他回调将在这两种方法之间调用,从而使协议实现中的资源管理更加容易。

以及状态机:

start 
-> connection_made
[-> data_received]*
[-> eof_received]?
-> connection_lost 
-> end

但是,函数EchoClientProtocol.connection_lost永远不会被调用, loop.run_until_complete(coro)在协议完成之前终止。

问题是:
如何获取协程/将来包装协议的方式,以便在达到协议的结束状态并且loop.run_until_complete返回此类事件时协程完成。

2 个答案:

答案 0 :(得分:2)

loop.run_until_complete(coro)返回transport, protocol

因此要触发connection_lost,应通过服务器或客户端关闭连接。所以你需要:

transport, _ = loop.run_until_complete(coro)
transport.close()
loop.run_forever()
print("done")

由于您不会在connection_lost中停止循环,因此它将永远被阻止在这里。

顺便说一句,coro将在成功连接连接后返回。

答案 1 :(得分:2)

正如Sraw的答案所指出的,loop.create_connection是一个协程,一旦创建了传输/协议对,它就会返回。因此,您需要运行另一个协程(或等效对象),以使事件循环保持活动状态,以使发生有趣的事情。

  

如何运行循环直到协议内部状态到达状态结束而无需显式关闭循环

您不一定需要关闭或停止循环。如果我正确地关注了您,则您希望避免使用难看的run_forever,而应编写以下内容:

transport, protocol = loop.run_until_complete(coro)
transport.close()
loop.run_until_complete(protocol.wait_connection_lost())

虽然wait_connection_lost()确实不是asyncio附带的,但是您正在提供协议实现,因此可以轻松地为您的协议创建一个协议:

class EchoClientProtocol(asyncio.Protocol):
    # your other methods are unchanged

    def __init__(self, message, loop):
        self.message = message.encode()
        self.__done = loop.create_future()

    def connection_lost(self, exc):
        # the value passed to set_result will be transmitted to
        # run_until_complete(protocol.wait_connection_lost()).
        self.__done.set_result(None)

    # When awaited, resumes execution after connection_lost()
    # has been invoked on this protocol.
    def wait_connection_lost(self):
        return self.__done