Python asyncio 设置接收到的mutliple asyncio.Protocol.data_的调用顺序

Python asyncio 设置接收到的mutliple asyncio.Protocol.data_的调用顺序,python-asyncio,Python Asyncio,当使用asyncio通过TCP套接字接收多个文件时,我很难确定接收到的\u数据的调用顺序。当一次发送3个数据流时,我的输出如下: DEBUG connection_made ('127.0.0.1', 33972) new connection was made DEBUG connection_made ('127.0.0.1', 33974) new connection was made DEBUG connection_made ('127.0.0.1', 33976) new conn

当使用asyncio通过TCP套接字接收多个文件时,我很难确定接收到的\u数据的调用顺序。当一次发送3个数据流时,我的输出如下:

DEBUG connection_made ('127.0.0.1', 33972) new connection was made
DEBUG connection_made ('127.0.0.1', 33974) new connection was made
DEBUG connection_made ('127.0.0.1', 33976) new connection was made
DEBUG data_received ('127.0.0.1', 33976) data received from new connection
DEBUG data_received ('127.0.0.1', 33974) data received from new connection
DEBUG data_received ('127.0.0.1', 33972) data received from new connection
import asyncio

class AIO(asyncio.Protocol):
    def __init__(self):
        self.extra = bytearray()

    def connection_made(self, transport):
        global request_time
        peer = transport.get_extra_info('peername')
        logger.debug('{} new connection was made'.format(peer))
        self.transport = transport
        request_time = str(time.time())

    def data_received(self, request, msgid=1):
        peer = self.transport.get_extra_info('peername')
        logger.debug('{} data received from new connection'.format(peer))
        self.extra.extend(request)
我假设它的行为类似于从最新到最旧的连接接收数据的堆栈,但这只是一个猜测

是否有可能改变这种行为,使数据按连接顺序接收?这很重要,因为我需要从第一个连接接收到的数据来进一步处理以下连接

我的代码如下:

DEBUG connection_made ('127.0.0.1', 33972) new connection was made
DEBUG connection_made ('127.0.0.1', 33974) new connection was made
DEBUG connection_made ('127.0.0.1', 33976) new connection was made
DEBUG data_received ('127.0.0.1', 33976) data received from new connection
DEBUG data_received ('127.0.0.1', 33974) data received from new connection
DEBUG data_received ('127.0.0.1', 33972) data received from new connection
import asyncio

class AIO(asyncio.Protocol):
    def __init__(self):
        self.extra = bytearray()

    def connection_made(self, transport):
        global request_time
        peer = transport.get_extra_info('peername')
        logger.debug('{} new connection was made'.format(peer))
        self.transport = transport
        request_time = str(time.time())

    def data_received(self, request, msgid=1):
        peer = self.transport.get_extra_info('peername')
        logger.debug('{} data received from new connection'.format(peer))
        self.extra.extend(request)

首先,我建议使用更高级的streams API,而不是传输/协议。然后,如果您需要维护建立连接时观察到的顺序,您可以使用一系列
asyncio.Event
s自己强制执行。例如:

import asyncio, itertools

class Comm:
    def __init__(self):
        self.extra = bytearray()
        self.preceding_evt = None

    async def serve(self, r, w):
        preceding_evt = self.preceding_evt
        self.preceding_evt = this_evt = asyncio.Event()
        while True:
            request = await r.read(4096)
            # wait for the preceding connection to receive and store data
            # before we store ours
            if preceding_evt is not None:
                await preceding_evt.wait()
                preceding_evt.clear()
            # self.extra now contains data from previous connections
            self.extra.extend(request)
            # inform the subsequent connection that we got data
            this_evt.set()
        w.close()

async def main():
    comm = Comm()
    server = await asyncio.start_server(comm.serve, '127.0.0.1', 8888)
    async with server:
        await server.serve_forever()

asyncio.run(main())

我不确定这里的行为是否像堆栈一样,它可能只是异步IO或内核中某些实现细节的结果。更重要的是,即使asyncio保证保留顺序,您如何确保在建立连接的同时接收数据?一般来说,无法保证首先建立的连接将首先获取一些数据。为什么订单对你很重要?也许你可以改变你的架构,这样精确的顺序就不重要了。首先谢谢你的回答。我不确定数据是否在同一时间接收,但连接是在同一时间打开的,或者至少是在彼此之后直接打开的。顺序很重要,因为我需要在第一个连接中发送数据,以便在第二个连接中进一步处理数据。我发布了一个答案(我认为)符合您的要求,但我不认为这有意义。服务器观察“在同一时间打开”的连接的顺序可能是完全随机的,这取决于内核或网络堆栈的详细信息,甚至异步IO实现的详细信息。如果您需要订购连接,您可能需要在客户端提供订单。我同意,那么客户端可能不会同时进行所有连接,而是直接在彼此之后进行,我理解错了。但是通过进一步的测试,连接和接收数据的顺序似乎是相同的。Comm()返回什么?还有,我如何将以前在eof_中收到的功能包含在这个解决方案的更高级别的streams API/中?@Daniel因为
Comm
是一个类,
Comm()
返回它的实例。在
main()
中创建的实例将在所有连接之间共享,就像问题代码中的
AIO
类一样。与
eof_received()
相当的是从
r.read()
@Daniel读取一个空字节对象。另外,请注意
r.read(4096)
将读取内核提供的数据量,但不超过4096字节。无法保证在一次读取中读取一个完整的请求,因此您可能希望采取步骤确保读取了整个响应。(方法将取决于请求的外观。)但为什么要分步阅读请求?无法使用
r读取整个响应。read()
?@Daniel
r.read()
将返回对等方发送的所有数据,直到他们关闭套接字的一侧,即文件结束。结果是,在
r.read()之后,无法读取任何其他内容,因此它不允许对等方通过同一连接继续与您通信。如果这对您有效,那就太好了,您还可以消除
while
循环。如果没有,您必须设计一种方法来检测您的请求在哪里结束。
r.read()
的另一个缺点是,不受信任的对等方可以向您提供无限数据,这将导致您的服务器内存不足。