【问题标题】:Twisted / Amp networking: Respond to all clients, not just one making a requestTwisted / Amp 网络:响应所有客户,而不仅仅是一个发出请求的客户
【发布时间】:2012-10-14 23:32:51
【问题描述】:

我正在尝试了解这些新奇的“工厂”风格网络库。 Twisted 广受好评,但对我来说绝对是一场噩梦,因为我不熟悉 lambda,因此我真的不知道如何遵循演示代码的操作。

演示客户端:

from twisted.internet import reactor, defer
from twisted.internet.protocol import ClientCreator
from twisted.protocols import amp
from ampserver import Sum, Divide


def doMath():
    d1 = ClientCreator(reactor, amp.AMP).connectTCP(
        '127.0.0.1', 1234).addCallback(
            lambda p: p.callRemote(Sum, a=13, b=81)).addCallback(
                lambda result: result['total'])
    def trapZero(result):
        result.trap(ZeroDivisionError)
        print "Divided by zero: returning INF"
        return 1e1000
    d2 = ClientCreator(reactor, amp.AMP).connectTCP(
        '127.0.0.1', 1234).addCallback(
            lambda p: p.callRemote(Divide, numerator=1234,
                                   denominator=0)).addErrback(trapZero)
    def done(result):
        print 'Done with math:', result
    defer.DeferredList([d1, d2]).addCallback(done)

if __name__ == '__main__':
    doMath()
    reactor.run()

演示服务器:

from twisted.protocols import amp

class Sum(amp.Command):
    arguments = [('a', amp.Integer()),
                 ('b', amp.Integer())]
    response = [('total', amp.Integer())]


class Divide(amp.Command):
    arguments = [('numerator', amp.Integer()),
                 ('denominator', amp.Integer())]
    response = [('result', amp.Float())]
    errors = {ZeroDivisionError: 'ZERO_DIVISION'}


class Math(amp.AMP):
    def sum(self, a, b):
        total = a + b
        print 'Did a sum: %d + %d = %d' % (a, b, total)
        return {'total': total}
    Sum.responder(sum)

    def divide(self, numerator, denominator):
        result = float(numerator) / denominator
        print 'Divided: %d / %d = %f' % (numerator, denominator, result)
        return {'result': result}
    Divide.responder(divide)


def main():
    from twisted.internet import reactor
    from twisted.internet.protocol import Factory
    pf = Factory()
    pf.protocol = Math
    reactor.listenTCP(1234, pf)
    print 'started'
    reactor.run()

if __name__ == '__main__':
    main()

据我了解,客户端p.callRemote(Sum, a=13, b=81)p.callRemote(Divide, numerator=1234, denominator=0) 部分调用Math.sum(13, 81)Math.Divide(1234, 0),因为工厂对象的protocol 设置为Math 类。不知何故,当客户端收到来自服务器的返回值时,子函数Done(result)被调用,将内容打印到屏幕上。

这很好,但是我的理解力很差,而且每篇文档似乎都已经期望达到这种理解水平了。

我真正想做的是将数据从客户端发送到服务器,然后从服务器发送到连接的多个客户端。此方法似乎在交换结束后立即忘记客户端,reactor.run() 阻塞客户端,阻止其执行任何其他工作。

人们可能每天都希望使用此功能。我怎么理解这个?

编辑:我尝试为客户端调用“签入”功能,但这似乎会使我的服务器淹没在请求中,只是为了响应“没有报告”。此外,它增加了延迟,因为客户端仅在他们请求时才收到新数据,而不是在它实际可用时。工厂 - 反应器布局似乎没有暴露我需要存储的客户信息以便任意响应它们。

【问题讨论】:

  • Lambda 是一种无需命名即可定义函数的方法;一个匿名函数。该文档显示了它们与功能的关系; docs.python.org/reference/expressions.html#lambda 话虽如此,我认为doMath() 中使用CLientCreator 的代码相当可怕。它以多种方式违反了“Python 之禅”(在交互式 Python 会话中尝试import this)。
  • @RolandSmith,您对 Twisted 示例质量的意见将通过twistedmatrix.com/trac/newticket 以文档错误报告的形式更有建设性地表达。我不会说文档是完美的,但提问者试图理解的示例的质量与问题并不真正相关。
  • @RolandSmith 这不是对 lambda 的严格准确定义,因为变量范围也有一些巧妙之处
  • @RolandSmith,谢谢import this,我没见过。作为一个自学成才的黑客,我总是害怕以错误的方式学习东西,所以我很珍惜这样的花絮。我会仔细尝试在 lambda 上提取文档。

标签: python networking twisted multiplayer


【解决方案1】:

你似乎有三个问题:

  1. lambda 在 Python 中是什么意思?”

    这是covered by Python's documentation。如果您仍然难以阅读以这种方式编写的代码,您可以使用 lambda x: y 只是编写 def my_function(x): return y 的快捷方式这一事实。任何你看到lambda的地方,例如,这个:

    def foo(bar):
        return boz().addCallback(lambda result: qux(bar, result))
    

    您可以随时将lambda 提取到它自己的函数中,以便您阅读,如下所示:

    def foo(bar):
        def callback_for_boz(result):
            return qux(bar, result)
        return boz().addCallback(callback_for_boz)
    
  2. How do I make input on one connection result in output on another?

    这是documented by Twisted's FAQ

  3. How do I make Twisted talk to multiple clients / connect to multiple servers?

    这是also a twisted FAQ。这里的总体思路是reactor.run() 的意思是“……然后运行整个程序”。在reactor.run() 之前运行任何代码的唯一原因是设置初始计时器、侦听套接字或第一个连接。您可以调用connectTCPlistenTCP 多次,无论是在您运行反应器之前还是在您程序中稍后发生的任何回调中。

【讨论】:

  • 感谢您提供的常见问题解答链接,自发布以来我偶然发现了#2 但不是#3,我会好好研究它们。对于您对 #3 的评论,我是否应该认为将 reactor.run() 包装在一个单独的线程中,通过队列等与扭曲的函数进行交互是不好的风格?
  • 我不愿称这种“坏”风格,因为这取决于您的要求,但在所有条件相同的情况下,通常最好“原生”使用 Twisted,然后运行 ​​reactor.run 一次主线程。在非主线程上运行它会中断子进程的产生,当你想关闭时,你会遇到各种噩梦般的信号/线程交互,通常是一个令人头疼的问题,你应该有充分的理由来处理它。此外,您不能使用Queue 以这种方式与 Twisted 交互:使用 reactor.callInThread/callfromThread
  • 如果你认为这回答了你的问题,顺便说一句,你应该“接受”这个答案(大的绿色复选标记)。
猜你喜欢
  • 1970-01-01
  • 2014-10-24
  • 2021-03-22
  • 2012-07-18
  • 2011-10-21
  • 2018-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多