【问题标题】:How to Create a Class for Every Asyncio Connection如何为每个 Asyncio 连接创建一个类
【发布时间】:2018-02-10 08:35:45
【问题描述】:

想想我有一个游戏,我正在为每个连接创建客户端类。客户端类存储用户偏好(如所选国家/地区)。并且当用户与游戏类断开连接时删除。我需要做这样的事情。我的代码:

import asyncio
import logging
import sys

SERVER_ADDRESS = ('localhost', 56346)

logging.basicConfig(
    level=logging.DEBUG,
    format='%(name)s: %(message)s',
    stream=sys.stderr,
)
log = logging.getLogger('main')

event_loop = asyncio.get_event_loop()

class Client():
    def __init__(this):
        this.something = ''

class Server(asyncio.Protocol):
    def connection_made(self, transport):
        self.transport = transport
        self.address = transport.get_extra_info('peername')
        self.log = logging.getLogger(
            'EchoServer_{}_{}'.format(*self.address)
        )
        self.log.debug('connection accepted')
    def data_received(self, data):
        self.log.debug('received {!r}'.format(data))
        self.transport.write(data)
        self.log.debug('sent {!r}'.format(data))
    def connection_lost(self, error):
        if error:
            self.log.error('ERROR: {}'.format(error))
        else:
            self.log.debug('closing')
        super().connection_lost(error)

factory = event_loop.create_server(Server, *SERVER_ADDRESS)
server = event_loop.run_until_complete(factory)
log.debug('starting up on {} port {}'.format(*SERVER_ADDRESS))
# Enter the event loop permanently to handle all connections.
try:
    event_loop.run_forever()
finally:
    log.debug('closing server')
    server.close()
    event_loop.run_until_complete(server.wait_closed())
    log.debug('closing event loop')
    event_loop.close()

【问题讨论】:

  • 你想要一个class还是一个object
  • 我想要一个存储用户数据的客户端类。每个用户都将拥有一个 Client 类。如果我进行 print(client.Username) 他们将看到自己的用户名。
  • 是的,但您的术语已关闭。类是代码,对象是运行该代码的实例。您要创建的东西显然是一个对象,而不是一个类(即不是新代码。实际上,那会很有趣!)
  • 创建一个Client 对象只是object = Client(parmeters...),但我无法立即告诉您希望在这堵代码墙的哪个位置发生这种情况。也许edit你的问题实际上是一个问题,并将代码简化为minimal reproducible example

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


【解决方案1】:

您将Server 作为protocol_factory 传递给create_server,这意味着将为每个客户端创建一个新的Server 实例。 (这个类更好的名字应该是ClientHandle。)

实例化客户端的逻辑位置在connection_made内部:

self.client = Client()

根据您正在实施的协议,可能会要求客户端自行授权。当客户端的属性到达您的data_received 方法时,您将填写它们。另请注意,您不是在为客户创建新的Clientclass,而是在创建新的Clientinstance

使用基于流的 API 实现通信要容易得多,它提供了到回调的协程映射。例如:

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

class App:
    def __init__(self):
        self._clients = set()

    async def new_client(self, reader, writer):
        addr = writer.get_extra_info('peername')
        writer.write(f'hello client from {addr}; please state your name\n'
                     .encode('utf-8'))
        name = (await reader.readline()).decode('utf-8').rstrip('\n')
        client = Client(name)
        client.reader = reader
        client.writer = writer
        writer.write(f'{name}, you are now represented as {client}\n'
                     .encode('utf-8'))
        # Add the client to the set of known clients. This would also
        # be the place to notify the application of the new client.
        # This coroutine could continue running and communicate
        # with the client without blocking the app.
        self._clients.add(client)

app = App()

loop = asyncio.get_event_loop()
loop.run_until_complete(
    asyncio.start_server(app.new_client, *SERVER_ADDRESS, loop=loop))

# serve the requests until someone calls loop.quit()
loop.run_forever()

【讨论】:

  • 什么是self._clients(有必要吗?),当用户与游戏断开连接时,客户端实例将如何被删除?
  • @EgehanAVCU 这是一组所有已连接的客户端,并非绝对必要,但您可能需要 some 形式的此类集合。您可以通过检查阅读器流上的at_eof 来发现客户端已死亡。可能有一种方法可以添加回调,以便在发生这种情况时立即调用。
猜你喜欢
  • 2012-08-14
  • 1970-01-01
  • 2018-01-19
  • 1970-01-01
  • 1970-01-01
  • 2019-05-09
  • 1970-01-01
  • 2017-04-26
  • 1970-01-01
相关资源
最近更新 更多