【问题标题】:Understanding python asyncio Protocol了解python异步协议
【发布时间】:2023-03-08 16:35:02
【问题描述】:

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

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

echo tcp 客户端示例代码:

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 在此类事件上返回。

【问题讨论】:

    标签: python python-asyncio


    【解决方案1】:

    正如 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 中停止循环,因此它将永远被阻止在这里。

      顺便说一句,连接成功时coro返回。

      【讨论】:

      • 感谢您的回答。你知道如何运行循环直到协议内部状态达到状态结束而不显式关闭循环。
      • 不,asyncio 中似乎没有这个接口。
      猜你喜欢
      • 2020-04-15
      • 2017-01-18
      • 1970-01-01
      • 1970-01-01
      • 2011-01-02
      • 1970-01-01
      • 1970-01-01
      • 2022-10-07
      • 1970-01-01
      相关资源
      最近更新 更多