Python3 Asyncio从初始连接创建辅助连接

时间:2017-05-31 21:38:31

标签: python python-3.x python-asyncio python-3.6

我正在尝试向asyncio循环添加两个协同程序并收到错误:

RuntimeError: This event loop is already running

我的目标是与服务器通信(我无法控制)。此服务器需要来自客户端的初始连接。然后,服务器在此连接上为客户端提供了一个端口。客户端必须使用此端口来创建第二个连接。服务器使用第二个连接向客户端发送未经请求的消息。对于其他双向通信,第一个连接始终保持不变。

要重新创建此方案,我有一些代码可以重现错误:

class Connection():
    def __init__(self, ip, port, ioloop):
        self.ip = ip
        self.port = port
        self.ioloop = ioloop
        self.reader, self.writer = None, None
        self.protocol = None
        self.fileno = None

    async def __aenter__(self):
        # Applicable when doing 'with Connection(...'
        log.info("Entering and Creating Connection")
        self.reader, self.writer = (
            await asyncio.open_connection(self.ip, self.port, loop=self.ioloop)
        )
        self.protocol = self.writer.transport.get_protocol()
        self.fileno = self.writer.transport.get_extra_info('socket').fileno()

        log.info(f"Created connection {self}")
        return self

    async def __aexit__(self, *args):
        # Applicable when doing 'with Connection(...'
        log.info(f"Exiting and Destroying Connection {self}")
        if self.writer:
            self.writer.close()

    def __await__(self):
        # Applicable when doing 'await Connection(...'
        return self.__aenter__().__await__()

    def __repr__(self):
        return f"[Connection {self.ip}:{self.port}, {self.protocol}, fd={self.fileno}]"

    async def send_recv_message(self, message):
        log.debug(f"send: '{message}'")
        self.writer.write(message.encode())
        await self.writer.drain()

        log.debug("awaiting data...")
        data = await self.reader.read(9999)
        data = data.decode()
        log.debug(f"recv: '{data}'")
        return data


class ServerConnection(Connection):
    async def setup_connection(self):
        event_port = 8889  # Assume this came from the server
        print("In setup connection")
        event_connection = await EventConnection('127.0.0.1', event_port, self.ioloop)
        self.ioloop.run_until_complete(event_connection.recv_message())

class EventConnection(Connection):
    async def recv_message(self):
        log.debug("awaiting recv-only data...")
        data = await self.reader.read(9999)
        data = data.decode()
        log.debug(f"recv only: '{data}'")
        return data


async def main(loop):
    client1 = await ServerConnection('127.0.0.1', 8888, loop)
    await client1.setup_connection()
    await client1.send_recv_message("Hello1")
    await client1.send_recv_message("Hello2")
    await asyncio.sleep(5)

if __name__ == '__main__':
    #logging.basicConfig(level=logging.INFO)
    logging.basicConfig(level=logging.DEBUG)
    log = logging.getLogger()
    ioloop = asyncio.get_event_loop()
    print('starting loop')
    ioloop.run_until_complete(main(ioloop))
    print('completed loop')
    ioloop.close()

在调用run_until_complete的ServerConnection.setup_connection()方法中发生错误。

由于缺乏对asyncio的理解,我可能做错了。基本上,如何在设置第一个连接时设置辅助连接以获取事件通知(未经请求)?

感谢。

跟进

由于代码非常相似(为了添加更多功能而进行了一些更改),我希望能够跟进到原始帖子并不是很糟糕,因为产生的错误仍然是相同的。

新问题是当它收到未经请求的消息(由EventConnection接收)时,recv_message调用process_data方法。我想让process_data成为未来,以便recv_message完成(ioloop应该停止)。然后,ensure_future将获取它并继续再次运行以使用ServerConnection对服务器执行请求/响应。在它做之前,它必须转到一些用户代码(由external_command()表示,我希望从中隐藏异步内容)。这将使它再次同步。因此,一旦他们完成了他们需要的工作,他们应该在ServerConnection上调用execute_command,然后再次启动循环。

问题是,我对使用ensure_future的期望并未平息,因为看起来循环似乎没有停止运行。因此,当代码执行到达执行run_until_complete的execute_command时,出现错误"此事件循环已经在运行"发生。

我有两个问题:

  1. 我怎样才能使ioloop在process_data之后停止 放入ensure_future,然后再次运行它 在execute_command?

  2. 一旦recv_message收到了什么,我们怎么能这样做呢 它可以收到更多未经请求的数据?只是使用它是否足够/安全 ensure_future再次召唤自己?

  3. 这是模拟此问题的示例代码。

    client1 = None
    
    class ServerConnection(Connection):
        connection_type = 'Server Connection'
        async def setup_connection(self):
            event_port = 8889  # Assume this came from the server
            print("In setup connection")
            event_connection = await EventConnection('127.0.0.1', event_port, self.ioloop)
            asyncio.ensure_future(event_connection.recv_message())
    
        async def _execute_command(self, data):
            return await self.send_recv_message(data)
    
        def execute_command(self, data):
            response_str = self.ioloop.run_until_complete(self._execute_command(data))
            print(f"exec cmd response_str: {response_str}")
    
        def external_command(self, data):
            self.execute_command(data)
    
    
    class EventConnection(Connection):
        connection_type = 'Event Connection'
        async def recv_message(self):
            global client1
            log.debug("awaiting recv-only data...")
            data = await self.reader.read(9999)
            data = data.decode()
            log.debug(f"recv-only: '{data}'")
            asyncio.ensure_future(self.process_data(data))
            asyncio.ensure_future(self.recv_message())
    
        async def process_data(self, data):
            global client1
            await client1.external_command(data)
    
    
    async def main(ioloop):
        global client1
        client1 = await ServerConnection('127.0.0.1', 8888, ioloop)
        await client1.setup_connection()
        print(f"after connection setup loop running is {ioloop.is_running()}")
        await client1.send_recv_message("Hello1")
        print(f"after Hello1 loop running is {ioloop.is_running()}")
        await client1.send_recv_message("Hello2")
        print(f"after Hello2 loop running is {ioloop.is_running()}")
        while True:
            print(f"inside while loop running is {ioloop.is_running()}")
            t = 10
            print(f"asyncio sleep {t} sec")
            await asyncio.sleep(t)
    
    
    if __name__ == '__main__':
        logging.basicConfig(level=logging.DEBUG)
        log = logging.getLogger()
        ioloop = asyncio.get_event_loop()
        print('starting loop')
        ioloop.run_until_complete(main(ioloop))
        print('completed loop')
        ioloop.close()
    

1 个答案:

答案 0 :(得分:0)

尝试更换:

self.ioloop.run_until_complete

使用

await