升级到Python3.12后,异步服务器和客户端脚本停止工作

问题描述 投票:0回答:1

所以我有两个使用异步服务器进行通信的脚本,脚本的工作方式是服务器打开异步服务器并侦听连接,客户端脚本连接到该服务器,服务器脚本停止侦听新连接并分配读取器和写入全局变量,以便可以发送和接收数据。

服务器.py:

import asyncio
import sys


class Server:
    def __init__(self):
        self.reader, self.writer = None, None
        self.connected = False

    async def listen(self, ip: str, port: int) -> None:
        """
        Listens for incoming connections and handles the first connection.
        After accepting the first connection, it stops the server from accepting further connections.

        :param ip: IP address to listen on.
        :param port: Port number to listen on.
        """

        async def handle_connection(reader, writer):
            print("Client connected!")
            # Assign the reader and writer to instance variables for later use
            self.reader, self.writer = reader, writer
            self.connected = True

            print("Shutting down server from accepting new connections")
            server.close()
            await server.wait_closed()


        print(f"Listening on {ip}:{port}")
        server = await asyncio.start_server(handle_connection, ip, port)

        try:
            async with server:
                await server.serve_forever()

        except KeyboardInterrupt:
            sys.exit(1)

        except asyncio.CancelledError:
            print("Connection canceled")
        except Exception as e:
            print(f"Unexpected error while trying to listen, Error: {e}")
            sys.exit(1)


if __name__ == '__main__':
    server = Server()
    asyncio.run(server.listen('192.168.0.35', 9090))

客户端.py:

import asyncio

class Client:
    def __init__(self):
        self.reader, self.writer = None, None
        self.connected = False

    async def connect(self, ip: str, port: int) -> None:
        """
        Connects to a server at the specified IP address and port.

        :param ip: IP address of the server.
        :param port: Port number of the server.
        """

        while not self.connected:
            try:
                self.reader, self.writer = await asyncio.wait_for(
                    asyncio.open_connection(ip, port), 5
                )
                print(f"Connecting to {ip}:{port}")
                self.connected = True
                break
            except Exception as e:
                print(
                    f"Failed to connect to {ip}:{port} retrying in 10 seconds."
                )
                print(e)
                await asyncio.sleep(10)
                continue


if __name__ == '__main__':
    Client = Client()
    asyncio.run(Client.connect('192.168.0.35', 9090))

在python 3.11中执行流程如下;客户端脚本正在连接到侦听服务器脚本,服务器脚本正在调用handle_connection函数,并且该函数正在引发

asyncio.CancelledError
,退出侦听方法并使读取器和写入器保持活动状态。

但是在 python 3.12 中;客户端脚本正在连接到侦听服务器脚本,服务器脚本正在调用handle_connection并且卡在

await server.wait_closed()

我做了一些调试,发现

await server.wait_closed()
行不会返回 除非使用 writer.close()
 关闭写入器
,这是我们不希望看到的,因为正如我所说,脚本将使用读取器和写入器进行通信。

我的预期操作是让服务器脚本侦听单个连接,当建立连接时,它会停止侦听任何进一步的连接尝试,但仍保持它与原始连接的客户端之间的连接。

编辑:我从 python3.11.9 升级到 python3.12.6

python python-3.x python-asyncio python-3.11 python-3.12
1个回答
0
投票

停止服务

server.close
就可以了。
wait_closed
有更广泛的含义。让我直接引用 asyncio 代码,它解释了一切,以及为什么它在 3.11 上工作:

    async def wait_closed(self):
        """Wait until server is closed and all connections are dropped.

        - If the server is not closed, wait.
        - If it is closed, but there are still active connections, wait.

        Anyone waiting here will be unblocked once both conditions
        (server is closed and all connections have been dropped)
        have become true, in either order.

        Historical note: In 3.11 and before, this was broken, returning
        immediately if the server was already closed, even if there
        were still active connections. An attempted fix in 3.12.0 was
        still broken, returning immediately if the server was still
        open and there were no active connections. Hopefully in 3.12.1
        we have it right.
        """
© www.soinside.com 2019 - 2024. All rights reserved.