Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/342.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
python中的asyncio是否支持用于UDP网络的基于协同路由的API?_Python_Python Asyncio - Fatal编程技术网

python中的asyncio是否支持用于UDP网络的基于协同路由的API?

python中的asyncio是否支持用于UDP网络的基于协同路由的API?,python,python-asyncio,Python,Python Asyncio,今晚我浏览了pythonasyncio模块文档,为我的一个课程项目寻找一些想法,但很快我发现python的标准aysncio模块中可能缺少功能 如果查看文档,您会发现有一个基于回调的API和一个基于协同路由的API。回调API可以用于构建UDP和TCP应用程序,而协同路由API似乎只能用于构建TCP应用程序,因为它使用了流式API 这给我带来了一个问题,因为我正在寻找一个用于UDP网络的基于协同路由的API,尽管我发现asyncio支持诸如和之类的低级基于协同路由的套接字方法,但是UDP网络的关

今晚我浏览了python
asyncio
模块文档,为我的一个课程项目寻找一些想法,但很快我发现python的标准
aysncio
模块中可能缺少功能

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

这给我带来了一个问题,因为我正在寻找一个用于UDP网络的基于协同路由的API,尽管我发现
asyncio
支持诸如和之类的低级基于协同路由的套接字方法,但是UDP网络的关键API,
recvfrom
sendto
并不存在

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

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完成。但遗憾的是,这些代码目前不可用,因为我们没有
asyncio
中的
socket.sendto
socket.recvfrom
的联合版本

我们可以在中实现的是使用传输协议回调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()
我们不能等待corroutine,因为回调不是corroutine,使用上面的阻塞IO调用将暂停回调中的控制流,并阻止循环处理任何其他事件,直到IO完成

另一个推荐的实现思想是在
data\u received
函数中创建
Future
对象,将其添加到事件循环中,并在协议类中存储任何需要的状态变量,然后显式地将控制返回到循环。虽然这可以工作,但它确实创建了许多复杂的代码,而在协同程序版本中,它们在任何方面都不需要

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

我想说的一点是,协程是一个非常好的设计,它可以在一个线程中利用并发的能力,同时也有一个非常简单的设计模式,可以节省人力和不必要的代码行,但是,让UDP网络工作的关键部分是我们的
asyncio
标准库中真正缺少的

你们怎么看

另外,如果有任何关于第三方库支持这种UDP网络API的其他建议,我将非常感谢我的课程项目。我发现它很像这样的东西,但它似乎并没有得到积极的维护

编辑:


似乎这确实实现了这个特性,但被
asyncio
开发人员拒绝了。开发人员声称,所有函数都可以使用协议传输API
create\u datagram\u endpoint()
实现。但正如我上面所讨论的,与在许多用例中使用回调API相比,协同路由API具有简单的功能,不幸的是,我们在UDP中没有这些功能。

没有提供基于流的API的原因是,流在回调的基础上提供排序,而UDP通信本质上是无序的,因此,这两者从根本上是不相容的

但这并不意味着你不能从你的回调中调用协同路由——事实上这很容易!从开始,您可以执行以下操作:

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\u received
启动您的
handle\u income\u数据包
corroutine,它可以免费等待任意数量的corroutine。由于协同程序在“后台”运行,因此事件循环在任何时候都不会被阻塞,并且收到的数据报会立即返回,正如预期的那样。

您可能会感兴趣:

在异步IO中提供易于使用的UDP套接字

以下是一个例子:

导入异步IO
导入异步UDP
异步def main():
sock=wait asyncudp.create_socket(远程地址=('127.0.0.1',9999))
sock.sendto(b'Hello!')
打印(等待sock.recvfrom())
sock.close()
asyncio.run(main())

是asyncore模块处理您所描述的内容,而不是asyncio@dmitryro。asyncore模块已被弃用,asyncio至少也处理UDP—请参阅示例。无论如何,asyncore都不会帮助OP,因为它的事件循环不支持协同路由。@user4815162342很高兴知道。谢谢。虽然我个人倾向于避免在设计模式中使用回调,但这个解决方案对我来说无疑是一个足够聪明的解决方案,可以最大限度地限制回调的使用。我接受这个。谢谢接下来的一个问题是,如果我倾向于使用带有
asyncio
的低级套接字接口,比如说我想发送ICMP数据包,那么使用
add_reader
是唯一的解决方案吗?@Chaserhkj在这种情况下,您可以将回调视为一个设计选择,而不仅仅是一个API入口点,仅仅是对协同程序世界的践踏。我从未使用过ICMP数据包,所以我不敢猜测,但这可能是另一个问题的素材。
async def main():
    # Create a local UDP enpoint
    local = await open_local_endpoint('localhost', 8888)

    # Create a remote UDP enpoint, pointing to the first one
    remote = await open_remote_endpoint(*local.address)

    # The remote endpoint sends a datagram
    remote.send(b'Hey Hey, My My')

    # The local endpoint receives the datagram, along with the address
    data, address = await local.receive()

    # Print: Got 'Hey Hey, My My' from 127.0.0.1 port 50603
    print(f"Got {data!r} from {address[0]} port {address[1]}")