Skip to main content
 首页 » 编程设计

python之为什么 asyncio.open_connection(或任何其他协程)会阻止 data_received 处理

2025年05月04日13tintown

我对 asyncio documentation 中的代码稍作修改

import asyncio 
asyncio.tasks._DEBUG = True 
 
 
class EchoServer(asyncio.Protocol): 
    def connection_made(self, transport): 
        name = transport.get_extra_info('sockname') 
        peername = transport.get_extra_info('peername') 
        print('Connection:',name,'<--',peername) 
        self.transport = transport 
 
    def data_received(self, data): 
        name = self.transport.get_extra_info('sockname') 
        peername = self.transport.get_extra_info('peername') 
 
        print('Got data:',name,'<--',peername,':',data.decode() ) 
        if name[1] == 8888: 
            print("Making connection") 
            reader, writer = yield from asyncio.open_connection('127.0.0.1', 8889, loop=asyncio.get_event_loop()) 
        else: 
            self.transport.write(data) 
 
        self.transport.close() 
 
 
loop = asyncio.get_event_loop() 
coro_1 = loop.create_server(EchoServer, '127.0.0.1', 8888) 
coro_2 = loop.create_server(EchoServer, '127.0.0.1', 8889) 
server_1 = loop.run_until_complete(coro_1) 
server_2 = loop.run_until_complete(coro_2) 
print('Serving on {}'.format(server_1.sockets[0].getsockname())) 
print('Serving on {}'.format(server_2.sockets[0].getsockname())) 
 
try: 
    loop.run_forever() 
except KeyboardInterrupt: 
    print("^C caught, exiting") 
finally: 
    server_1.close() 
    server_2.close() 
    loop.close() 

但是,当我运行它时,它似乎甚至没有运行 data_received 函数:

Serving on ('127.0.0.1', 8888) 
Serving on ('127.0.0.1', 8889) 
Connection: ('127.0.0.1', 8888) <-- ('127.0.0.1', 36580) 

但是当我注释掉 yield from 行时,它按照我期望的方式运行。

Serving on ('127.0.0.1', 8888) 
Serving on ('127.0.0.1', 8889) 
Connection: ('127.0.0.1', 8888) <-- ('127.0.0.1', 36581) 
Got data: ('127.0.0.1', 8888) <-- ('127.0.0.1', 36581) : Hi, gaise! 
Making connection 

我试过添加:

@asyncio.coroutine 
def do_a_thing(): 
    print("hey, it's a thing!") 
 
def try_stuff(): 
    print('Trying') 
    yield from asyncio.async(do_a_thing()) 

并改为调用 try_stuff。这会输出“建立连接”,但不会输出“正在尝试”。通过将 yield from 行更改为一个简单的调用,可以输出“Trying”。如果我从 do_a_thing 中删除协程装饰器,那么我会得到输出。

对我来说重要的是我正在尝试创建到第二台服务器的异步连接。但显然,yield from 的存在阻止了任何进一步的处理(至少我可以告诉 - 它肯定会随着打印而停止)。

那么我做错了什么,我该如何解决这个问题?

请您参考如下方法:

yield from必须在协程中使用。如我所见,由于某些 asyncio 内部结构,data_received 不能成为协程。

一个解决方案是将您的代码包装在协程中,并使用 asyncio.Task() 调用它。

作为示例,您可以查看我的 experiments