如何使用 asyncio.Protocol 在 python asyncio 中创建和 运行 两个或多个(多个)服务器

How to create and run two or more (multiple) servers in python asyncio with asyncio.Protocol

我想在 python (Python 3.9.9) 中使用 asyncio 在一个脚本中 运行 两个简单的 TCP 服务器。显然,每个服务器 运行 位于不同的端口。我在使用正确的语法来同时创建和启动两个服务器时遇到了问题。以下是我目前所拥有的,但我不断收到 RuntimeError: This event loop is already 运行ning.

我已经看到仅使用一个函数和 loop.start_server 即可完成此操作的方法,但我需要使用 asyncio.Protocol class 以便我可以访问回调conection_made 和 connection_lost。我不知道我是否需要 loop.create_server 或其他一些语法,但无论我做什么显然都是错误的。那么我该如何使用 asyncio.Protocol?

注意:这两个服务器都是为了 运行 永远。客户端可以连接和断开连接,但服务器应该 运行 永远。

###############################################################################
# echoserver.py
# Simple code to run EchoServer on two ports simultaneously
###############################################################################
import asyncio

FIRST_PROCESS_PORT =14401
SECOND_PROCESS_PORT =14402

class MyLogger(object):
    def info(self,s):
        print(s)
logger = MyLogger()

#
# EchoServer class
#
class EchoServer(asyncio.Protocol):
    def __init__(self):
        self.client_info=None

    # Begin asyncio.Protocol overrides
    def connection_made(self,transport):
        self.transport = transport
        self.client_info = self.transport.get_extra_info('peername')
        logger.info('connection_made from: %s' % self.client_info)

    def connection_lost(self, reason):
        logger.info( 'connection_lost: %s | %s' % (self.client_info, reason))

    def data_received(self,data):
        print("Received: %s" % str(data))
        self.transport.write(data)

async def main():
    global server1
    global server2

    loop = asyncio.get_running_loop()

    factory1 = await loop.create_server(lambda: EchoServer(),"localhost",FIRST_PROCESS_PORT)
    factory2 = await loop.create_server(lambda: EchoServer(),"localhost",SECOND_PROCESS_PORT)
    server1 = await loop.run_until_complete(factory1)
    server2 = await loop.run_until_complete(factory2)

    async with server1, server2:
        await asyncio.gather(server1.run_forever(),server2.run_forever())

    print("Main done");

###################
# main code here
###################
if __name__ == "__main__":
    asyncio.run(main())
    print("Done");

我做了一些简单的重新安排,使服务器在两个端口上工作:

###############################################################################
# echoserver.py
# Simple code to run EchoServer on two ports simultaneously
###############################################################################
import asyncio

FIRST_PROCESS_PORT = 14401
SECOND_PROCESS_PORT = 14402


class MyLogger(object):
    def info(self, s):
        print(s)


logger = MyLogger()


class EchoServer(asyncio.Protocol):
    def __init__(self):
        self.client_info = None

    # Begin asyncio.Protocol overrides
    def connection_made(self, transport):
        self.transport = transport
        self.client_info = self.transport.get_extra_info("peername")
        logger.info(f"connection_made from: {self.client_info}")

    def connection_lost(self, reason):
        logger.info("connection_lost: %s | %s" % (self.client_info, reason))

    def data_received(self, data):
        print("Received: %s" % str(data))
        self.transport.write(data)


async def main():
    loop = asyncio.get_running_loop()

    server1 = await loop.create_server(
        EchoServer, "0.0.0.0", FIRST_PROCESS_PORT
    )
    server2 = await loop.create_server(
        EchoServer, "0.0.0.0", SECOND_PROCESS_PORT
    )

    # serve forever
    async with server1, server2:
        while True:
            await asyncio.sleep(3600)


if __name__ == "__main__":
    asyncio.run(main())

这将在所有接口 (0.0.0.0) 上创建两个端口,但您可以将其更改为 localhost

在其他终端上,如果你 运行:

echo "Hello World" | nc -N <your IP address> 14401
echo "Hello World" | nc -N <your IP address> 14402

服务器响应:

connection_made from: ('XXX', 41760)
Received: b'Hello World\n'
connection_lost: ('XXX', 41760) | None
connection_made from: ('XXX', 32872)
Received: b'Hello World\n'
connection_lost: ('XXX', 32872) | None