我对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_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返回此类事件时协程完成。
答案 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