Python 使用asyncore创建具有客户机/服务器模型的交互式会话

Python 使用asyncore创建具有客户机/服务器模型的交互式会话,python,session,asynchronous,twisted,asyncore,Python,Session,Asynchronous,Twisted,Asyncore,我正在尝试创建一个程序,允许多个客户端同时连接到一台服务器。这些连接在服务器端应该是交互式的,这意味着在客户端连接之后,我可以从服务器向客户端发送请求 下面的asyncore示例代码只是用回音回复,我需要一种交互访问每个会话的方式,而不是回音。在我决定与之互动之前,我会对每个连接进行背景设置。如果我有100个会话,我想选择一个特定的会话,或者选择所有会话或会话的子集来发送命令。此外,我也不是100%确定asyncore库是否是实现此目的的方法,非常感谢您的帮助 import asyncore i

我正在尝试创建一个程序,允许多个客户端同时连接到一台服务器。这些连接在服务器端应该是交互式的,这意味着在客户端连接之后,我可以从服务器向客户端发送请求

下面的asyncore示例代码只是用回音回复,我需要一种交互访问每个会话的方式,而不是回音。在我决定与之互动之前,我会对每个连接进行背景设置。如果我有100个会话,我想选择一个特定的会话,或者选择所有会话或会话的子集来发送命令。此外,我也不是100%确定asyncore库是否是实现此目的的方法,非常感谢您的帮助

import asyncore
import socket

class EchoHandler(asyncore.dispatcher_with_send):

    def handle_read(self):
        data = self.recv(8192)
        if data:
            self.send(data)

class EchoServer(asyncore.dispatcher):

    def __init__(self, host, port):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind((host, port))
        self.listen(5)

    def handle_accept(self):
        pair = self.accept()
        if pair is not None:
            sock, addr = pair
            print 'Incoming connection from %s' % repr(addr)
            handler = EchoHandler(sock)

server = EchoServer('localhost', 8080)
asyncore.loop()
审查要求 你想要

  • 客户端/服务器方式的远程调用
  • 可能使用TCP通信
  • 在通话中使用会话

不太清楚,您真的想使用会话,所以我会考虑,会话只是调用参数之一,它在服务器端以及客户端都有一定意义,并且跳过实现它。

zmq
作为简单可靠的远程消息传递平台 ZeroMQ是轻量级消息传递平台,不需要复杂的服务器基础结构。它可以处理许多消息传递模式,下面的示例显示了使用多部分消息的请求/应答模式

有许多替代方案,您可以使用编码为JSON等格式的简单消息,也可以使用多部分消息跳过

server.py
玩玩具 启动服务器:

$ python server.py 
现在它运行并等待请求

现在启动客户端:

$ python client.py 
------------------
rqmsg Hi There
response Echo your not urgent message: 'Hi There'
------------------
rqmsg ['Hi There', 'very URGENT']
response Echo your very URGENT message: 'Hi There'
------------------
too_short_call
response Bad request, 1-2 arguments expected.
------------------
现在做一点实验:

  • 先启动客户端,然后启动服务器
  • 在处理过程中停止服务器,稍后重新启动
  • 启动多个客户端
所有这些场景都应由
zmq
处理,无需添加额外的Python代码行

结论 ZeroMQ提供了非常方便的远程消息传递解决方案,可以尝试计算消息传递相关代码的行数,并与任何其他解决方案进行比较,从而提供相同级别的稳定性

会话(属于OP的一部分)可以看作是调用的额外参数。正如我们所看到的,多个参数不是问题


在维护会话时,可以使用不同的后端,它们可以位于内存(对于单个服务器实例)、数据库或memcache或Redis中。这个答案没有详细说明会话,因为不太清楚预期的用途。

这是一个Twisted服务器:

import sys

from twisted.internet.task import react
from twisted.internet.endpoints import serverFromString
from twisted.internet.defer import Deferred
from twisted.internet.protocol import Factory

from twisted.protocols.basic import LineReceiver

class HubConnection(LineReceiver, object):
    def __init__(self, hub):
        self.name = b'unknown'
        self.hub = hub

    def connectionMade(self):
        self.hub.append(self)

    def lineReceived(self, line):
        words = line.split(" ", 1)
        if words[0] == b'identify':
            self.name = words[1]
        else:
            for connection in self.hub:
                connection.sendLine("<{}> {}".format(
                    self.name, line
                ).encode("utf-8"))

    def connectionLost(self, reason):
        self.hub.remove(self)

def main(reactor, listen="tcp:4321"):
    hub = []
    endpoint = serverFromString(reactor, listen)
    endpoint.listen(Factory.forProtocol(lambda: HubConnection(hub)))
    return Deferred()

react(main, sys.argv[1:])

它实现了一个基本的聊天服务器。希望代码是相当不言自明的;您可以在一个终端中使用
pythonhub\u server.py
运行它进行测试,在第二个终端中使用
pythonhub\u client.py alice
运行,在第三个终端中使用
pythonhub\u client.py bob
;然后输入alice和bob的会话,您可以看到它的作用。

我有点困惑,您将如何使用会话。通常,会话是一个概念,用于保存有关客户端和服务器之间过去通信的更广泛信息。但您所说的是通过一个请求处理多个会话,这肯定是不同的(在会话方面是不寻常的)。您的客户机/服务器请求听起来像请求/回复,但寻址多个会话听起来像是分派任务的工作人员(不清楚您希望返回什么作为响应)。Jan,我正在寻找一种(从服务器端)与所有代理交互的方法。这些代理将回调同一ip:端口对上的一台服务器。在服务器上,所有活动连接的日志应该通过一些命令存在,这也是通过s.send()与它们交互的一种方式。我的愿景是,100名代理随时回拨到我的服务器并保持连接。几个小时后,我来到我的服务器,键入(list),它会给我一个活动连接的列表,然后我选择与1个连接交互并发送一个dir请求。代理将结果发送给我。我意识到s.listen()具有积压连接的能力,有没有办法将这些连接放在最前面并积压活动连接,反之亦然?这可能就是我所需要的。我明白了,会话扮演着连接的角色。我现在觉得这个要求很清楚。这不是一个微不足道的消息传递任务,但它应该是可能的。我会提出一些小的改变——在<代码>列表的时刻,不要计算活的连接,但最好让客户报告,他们真的还活着。这将简化我脑海中的解决方案。我希望提供简单的基于zmq的解决方案,但整个实验变得有点太复杂,我放弃了。有太多的困难,主要是由于相当复杂的消息传递场景。从zeromq指南中的负载平衡示例开始,然后转到异步请求-应答,最后在匹配异步请求和应答时迷失方向。
$ python client.py 
------------------
rqmsg Hi There
response Echo your not urgent message: 'Hi There'
------------------
rqmsg ['Hi There', 'very URGENT']
response Echo your very URGENT message: 'Hi There'
------------------
too_short_call
response Bad request, 1-2 arguments expected.
------------------
import sys

from twisted.internet.task import react
from twisted.internet.endpoints import serverFromString
from twisted.internet.defer import Deferred
from twisted.internet.protocol import Factory

from twisted.protocols.basic import LineReceiver

class HubConnection(LineReceiver, object):
    def __init__(self, hub):
        self.name = b'unknown'
        self.hub = hub

    def connectionMade(self):
        self.hub.append(self)

    def lineReceived(self, line):
        words = line.split(" ", 1)
        if words[0] == b'identify':
            self.name = words[1]
        else:
            for connection in self.hub:
                connection.sendLine("<{}> {}".format(
                    self.name, line
                ).encode("utf-8"))

    def connectionLost(self, reason):
        self.hub.remove(self)

def main(reactor, listen="tcp:4321"):
    hub = []
    endpoint = serverFromString(reactor, listen)
    endpoint.listen(Factory.forProtocol(lambda: HubConnection(hub)))
    return Deferred()

react(main, sys.argv[1:])
import sys

from twisted.internet.task import react
from twisted.internet.endpoints import clientFromString
from twisted.internet.defer import Deferred, inlineCallbacks
from twisted.internet.protocol import Factory
from twisted.internet.stdio import StandardIO

from twisted.protocols.basic import LineReceiver
from twisted.internet.fdesc import setBlocking

class HubClient(LineReceiver):
    def __init__(self, name, output):
        self.name = name
        self.output = output

    def lineReceived(self, line):
        self.output.transport.write(line + b"\n")

    def connectionMade(self):
        self.sendLine("identify {}".format(self.name).encode("utf-8"))

    def say(self, words):
        self.sendLine("say {}".format(words).encode("utf-8"))

class TerminalInput(LineReceiver, object):
    delimiter = "\n"
    hubClient = None
    def lineReceived(self, line):
        if self.hubClient is None:
            self.output.transport.write("Connecting, please wait...\n")
        else:
            self.hubClient.sendLine(line)

@inlineCallbacks
def main(reactor, name, connect="tcp:localhost:4321"):
    endpoint = clientFromString(reactor, connect)
    terminalInput = TerminalInput()
    StandardIO(terminalInput)
    setBlocking(0)
    hubClient = yield endpoint.connect(
        Factory.forProtocol(lambda: HubClient(name, terminalInput))
    )
    terminalInput.transport.write("Connecting...\n")
    terminalInput.hubClient = hubClient
    terminalInput.transport.write("Connected.\n")
    yield Deferred()

react(main, sys.argv[1:])