gpt4 book ai didi

python - 来自 python 的 asyncio 是否支持用于 UDP 网络的基于协程的 API?

转载 作者:太空狗 更新时间:2023-10-29 21:51:53 25 4
gpt4 key购买 nike

今晚我正在浏览 python asyncio 模块文档,为我的一个类(class)项目寻找一些想法,但我很快发现 python 的标准 aysncio 中可能缺少一些功能 模块。

如果您查看文档,您会发现有一个基于回调的 API 和一个基于协程的 API。回调 API 可用于构建 UDP 和 TCP 应用程序,而协程 API 看起来只能用于构建 TCP 应用程序,因为它使用流式 API。

这对我来说是个问题,因为我一直在寻找用于 UDP 网络的基于协程的 API,尽管我确实发现 asyncio 支持基于低级协程的套接字方法,如 sock_recvsock_sendall ,但 UDP 网络的关键 API,recvfromsendto 不存在。

我想做的是编写一些代码,例如:

async def handle_income_packet(sock):
await data, addr = sock.recvfrom(4096)
# data handling here...
await sock.sendto(addr, response)

我知道这可以使用回调 API 等效地实现,但这里的问题是回调不是协程而是常规函数,因此在其中您无法将控制权交还给事件循环并保留函数执行状态。

看看上面的代码,如果我们需要在数据处理部分做一些阻塞IO操作,我们在协程版本中不会有问题,只要我们的IO操作也是在协程中完成的:

async def handle_income_packet(sock):
await data, addr = sock.recvfrom(4096)
async with aiohttp.ClientSession() as session:
info = await session.get(...)
response = generate_response_from_info(info)
await sock.sendto(addr, response)

只要我们使用await,事件循环就会从那个点获取控制流来处理其他事情,直到该 IO 完成。但遗憾的是,这些代码目前可用,因为我们在 中没有 socket.sendtosocket.recvfrom 的协程版本>异步

我们可以在其中实现的是使用传输协议(protocol)回调 API:

class EchoServerClientProtocol(asyncio.Protocol):
def connection_made(self, transport):
peername = transport.get_extra_info('peername')
self.transport = transport

def data_received(self, data):
info = requests.get(...)
response = generate_response_from_info(info)
self.transport.write(response)
self.transport.close()

我们不能在那里 await 协程,因为回调不是协程,并且像上面那样使用阻塞 IO 调用会停止回调中的控制流,并阻止循环处理任何其他事件,直到 IO完成

另一种推荐的实现思路是在data_received函数中创建一个Future对象,将其加入到事件循环中,并在Protocol类中存储任何需要的状态变量,然后显式地将控制返回给循环。虽然这可行,但它确实会创建很多复杂的代码,而在协程版本中,它们根本不需要。

还有 here我们有一个使用非阻塞套接字和 add_reader 处理 UDP 套接字的示例。但是和coroutine-version的几行代码相比,代码看起来还是很复杂。

我想说的是,协程是一个非常好的设计,可以在一个线程中利用并发的力量,同时也有一个非常简单的设计模式,可以节省脑力和不必要的代码行,但关键在我们的 asyncio 标准库中确实缺少使其适用于 UDP 网络的部分。

大家怎么看这件事?

此外,如果对支持此类 UDP 网络 API 的第 3 方库有任何其他建议,为了我的类(class)项目,我将非常感激。我找到了 Bluelet挺像这样的东西但是貌似没有主动维护。

编辑:

看来这个PR确实实现了此功能,但被 asyncio 开发人员拒绝了。开发人员声称所有功能都可以使用协议(protocol)传输 API create_datagram_endpoint() 来实现。但正如我上面所讨论的,与在许多用例中使用回调 API 相比,协程 API 具有简单的力量,很遗憾我们在 UDP 中没有这些。

最佳答案

未提供基于流的 API 的原因是流在回调之上提供排序,而 UDP 通信本质上是无序的,因此两者根本不兼容。

但这并不意味着您不能从回调中调用协同程序——事实上这很容易!从 EchoServerProtocol example 开始,你可以这样做:

def datagram_received(self, data, addr):
loop = asyncio.get_event_loop()
loop.create_task(self.handle_income_packet(data, addr))

async def handle_income_packet(self, data, addr):
# echo back the message, but 2 seconds later
await asyncio.sleep(2)
self.transport.sendto(data, addr)

此处 datagram_received 启动您的 handle_income_packet 协程,它可以自由等待任意数量的协程。由于协程在“后台”运行,事件循环在任何时候都不会被阻塞,datagram_received 会立即返回,正如预期的那样。

关于python - 来自 python 的 asyncio 是否支持用于 UDP 网络的基于协程的 API?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48621360/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com