首页 文章

了解python asyncio协议

提问于
浏览
1

我对Python asyncio和协议如何工作缺乏了解 .

看似相关的Documentaion:
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")

在echo服务器上同时输出:

write
Data received: {!r} 12
write
done

根据我的理解,这应该运行直到连接关闭 .

来自:Connection callbacks

每次成功连接时,connect_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 回答

  • 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
    
  • 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 中停止循环,它将永远被阻止 .

    BTW, coro 在连接成功连接时返回 .

相关问题