【问题标题】:TCP Server to communicate with Client on demandTCP Server 按需与 Client 通信
【发布时间】:2020-10-28 14:58:41
【问题描述】:

有没有办法记住所有客户端实例(例如在字典中),并按需向任何需要的客户端发送消息/通信?

import asyncio

class EchoServerClientProtocol(asyncio.Protocol):
    def connection_made(self, transport):
        self.transport = transport
        self.peername = transport.get_extra_info('peername')
        
        print('Connection from {}'.format(self.peername))

    def data_received(self, data):
        message = data.decode()
        print('Data received: {!r}'.format(message))

        print('Send: {!r}'.format(message))
        self.transport.write(data)

    def connection_lost(self, exc):
        print('Lost connection of {}'.format(self.peername))
        self.transport.close()

loop = asyncio.get_event_loop()
coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888)
server = loop.run_until_complete(coro)

print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
    loop.run_forever()
except KeyboardInterrupt:
    pass

server.close()
loop.run_until_complete(server.wait_closed())
loop.close()

【问题讨论】:

  • 这是客户端-服务器连接,它期望只有客户端发送请求,服务器只发送这些请求的答案。您尝试用客户端替换服务器 - 您的服务器想要发送请求(如客户端)并且客户端必须接收它(如服务器)。在普通的客户端-服务器中你不能这样做——人们在客户端使用循环,它会定期向服务器发送请求以询问是否有新消息。在网络浏览器中,他们开始使用websockets 按需从服务器发送到客户端。但它可能需要不同的模块然后客户端-服务器
  • @furas 这将用于从 PY 应用程序将 json 数据发送到收据打印机,因此不能使用 websockets。在客户端上运行循环可能会非常费力,因为服务器需要一直检查数据库。它不会是即时的。而不是依赖于来自数据库的触发器通知,然后发送 json。
  • 那么您的客户端必须作为服务器运行并等待来自您的 PY 应用程序的请求,该应用程序必须作为客户端运行。
  • 你展示了 ClientServerProtocol 所以我虽然你使用方法客户端-服务器。但是如果你使用套接字并且你可以使用任何你喜欢的东西,那么你的服务器和客户端可以双向运行——它们可以同时发送和接收。它可能需要在单独的线程中运行它(一个用于发送,另一个用于接收),因为它可能会阻塞。或者,在开始接收之前,您必须使用模块 select 来检查套接字上是否有要接收的内容。
  • 有两个线程,它看起来像两个不同方向的客户端-服务器连接。也许我在GitHub/python-examples/socket 上有一些示例代码,但我不记得了。但我没有select 的版本

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


【解决方案1】:

如果我正确理解您的问题,您希望能够维护连接图,然后稍后将数据发送到连接。

import asyncio
from functools import partial
from random import choice, randint

class Echo(asyncio.Protocol):
    def __init__(self, client_map):
        self.client_map = client_map

    def connection_made(self, transport):
        self.transport = transport
        peername = transport.get_extra_info("peername")
        print(f"[x] connected to {peername}")
        self.client_map[transport.get_extra_info("peername")] = self

    def connection_lost(self, exc):
        peername = self.transport.get_extra_info("peername")
        print(f"[!] disconnected from {peername}")
        del self.client_map[peername]

    def data_received(self, data):
        print(f"<<< {data}")
        self.transport.write(b">>> " + data)

def protocol_factory(client_map):
    proto = Echo(client_map)
    return proto

def random_hello(loop, client_map):
    delay = randint(1, 10)
    if len(client_map) > 0:
        peername = choice(sorted(client_map))
        client_map[peername].transport.write(b">>> random hello\n")

    loop.call_later(
        delay, random_hello, loop, client_map,
    )

async def main():
    loop = asyncio.get_running_loop()
    client_map = {}
    _protocol_factory = partial(protocol_factory, client_map=client_map)
    server = await loop.create_server(_protocol_factory, "127.0.0.1", 8888,)
    random_hello(loop, client_map)
    async with server:
        await server.serve_forever()

asyncio.run(main())

使这一切正常运行的是protocol_factory()。你传入一个client_map: dict,它将成为所有协议对象中的共享变量,并且当客户端连接时,协议对象将存储在peername 键下。 random_hello() 函数演示了向协议发送数据。在您希望能够访问连接的客户端并通过网络发送数据的任何地方传递client_map

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-26
    • 2014-02-28
    • 2015-12-13
    相关资源
    最近更新 更多