1

我刚刚被介绍给扭曲通过这个聊天应用程序的好教程,但我不确定如何使其适应我的需要。

假设一时兴起,我希望聊天服务器向所有客户端发送一条友好的消息,例如“长周末快乐!”。即我想让反应堆运行一些东西,但是在它已经运行之后(所以我不能提前安排它,或者我不想)。

我想做这样的事情:

def do_something():
    # do something

# setup and run reactor
factory = Factory()
factory.clients = []
factory.protocol = MyServer 
reactor.listenTCP(80, factory)
reactor.run() # asynchronously?

# clients connect...

reactor.callLater(0, do_something)

我尝试使用 python 线程,但它没有工作。我查看了这些扭曲的示例,但它们最后都有 reactor.run() 语句,这让我大吃一惊。是的,我很可能错过了一些基本的东西(这就是我在这里的原因)。

4

3 回答 3

2

您无需更改运行反应器的方式即可实现此行为。

相反,只需认识到程序中的所有内容都是对某些事件的响应。

你什么时候发出“长周末快乐”的通知?当然,当一个长周末即将开始时。换句话说,日历(它只是一种特殊的计时设备)会生成一个事件,然后你对其做出反应。您可以使用以下方法实现IReactorTime.callLater:计算直到下一个长周末的时间和reactor.callLater(that_delay, some_function)

如果您想在用户单击按钮时做某事,那就是对 GUI 库生成的事件的响应。如果您想在连接 USB 设备时执行某些操作,那是对平台 HAL(或类似 DBUS 或 udev)生成的事件的响应。

每当您认为您“自行行动”时,请考虑一下它为什么会采取行动——在什么条件下或响应什么情况——你就会开始弄清楚它实际上对什么事件做出反应。

于 2014-05-17T18:42:29.737 回答
1

你说过:

假设一时兴起,我想……向所有客户发送友好的信息

我想当我坐在服务器上时,我想要做的是在 Python 中以交互方式生成事件。

我将把它翻译成“我想在我的反应堆运行时有一个键盘界面”,并给你一个例子。

在 Twisted 中,键盘只是另一个 IO 接口,您可以与所有其他 IO 一起使用,我将提供的示例适用于 unix/posix 类型的平台,尽管同样的想法当然也可以在其他操作系统上实现。

(免责声明:这个例子有点乱,因为它在 tty 上设置了 cbreak 模式,这是我喜欢为交互式控制做的事情,但它肯定不是必需的。)

#!/usr/bin/python

import sys # so I can get at stdin
import os # for isatty
import termios, tty # access to posix IO settings
from twisted.internet import reactor
from twisted.internet import stdio # the stdio equiv of listenXXX
from twisted.protocols import basic # for lineReceiver for keyboard
from twisted.internet.protocol import Protocol, ServerFactory

class Cbreaktty(object):
    org_termio = None
    my_termio = None

    def __init__(self, ttyfd):
        if(os.isatty(ttyfd)):
            self.org_termio = (ttyfd, termios.tcgetattr(ttyfd))
            tty.setcbreak(ttyfd)
            print '  Set cbreak mode'
            self.my_termio = (ttyfd, termios.tcgetattr(ttyfd))
        else:
          raise IOError #Not something I can set cbreak on!

    def retToOrgState(self):
        (tty, org) = self.org_termio
        print '  Restoring terminal settings'
        termios.tcsetattr(tty, termios.TCSANOW, org)


class MyClientConnections(Protocol):
    def connectionMade(self):
        print "Got new client!"
        self.factory.clients.append(self)

    def connectionLost(self, reason):
        print "Lost a client!"
        self.factory.clients.remove(self)

class MyServerFactory(ServerFactory):
    protocol = MyClientConnections

    def __init__(self):
        self.clients = []

    def sendToAll(self, message):
      for c in self.clients:
        c.transport.write(message)

    def hello_to_all(self):
        self.sendToAll("A friendly message, sent on a whim\n")
        print "sending friendly..."

class KeyEater(basic.LineReceiver):

    def __init__(self, hello_callback):
        self.setRawMode() # Switch from line mode to "however much I got" mode
        self.hello_to_all = hello_callback

    def rawDataReceived(self, data):
        key = str(data).lower()[0]
        if key == 's':
            self.hello_to_all()
        elif key == 'q':
            reactor.stop()
        else:
            print "Press 's' to send a message to all clients, 'q' to shutdown"

def main():
    client_connection_factory = MyServerFactory()

    try:
      termstate = Cbreaktty(sys.stdin.fileno())
    except IOError:
      sys.stderr.write("Error: " + sys.argv[0] + " only for use on interactive ttys\n")
      sys.exit(1)

    keyboardobj = KeyEater(client_connection_factory.hello_to_all)

    stdio.StandardIO(keyboardobj,sys.stdin.fileno())
    reactor.listenTCP(5000, client_connection_factory)
    reactor.run()
    termstate.retToOrgState()
if __name__ == '__main__':
  main()

如果您运行上述代码(.. 假设您在 unix/posix 上),您将拥有一个反应器,它既等待/服务 TCP 连接,又等待密钥在标准输入上发生。键入键 's' 将向所有连接的客户端发送消息。

这是我经常用于对扭曲应用程序进行异步控制的方法,尽管它肯定只是其他答案中提到的众多方法之一。

于 2014-05-21T02:22:35.250 回答
1

正如 JP 所说,反应堆响应事件。假设您在 Linux/Unix 系统上运行:

  • 您可以从 Twisted 工厂或协议中启动一个线程,并让该线程使用 reactor.callInThread() 和 reactor.callFromThread() 发回命令
  • 或者,您可以从由 callWhenRunning 启动的函数设置 callLater 以运行检查文件或目录或其他任何内容的函数,并在找到时执行某些操作。callLater 运行的函数可能会为后续操作设置另一个 callLater。
  • 或者您可以在 Twisted 工厂内(甚至从协议内)设置循环调用
  • 或者,您的聊天服务器可以监视来自客户端的特殊字符序列,指示服务器执行某些操作(例如向所有登录的客户端发送附加消息
  • 或者您的服务器可以(从扭曲的内部)监视 ip 或 unix 'command' 套接字,监视导致发送消息的命令。
  • 在您的上下文中,事件可能是由 Unix 信号处理程序发起的操作,尽管我记得 Twisted 在内部做了一些事情来拦截信号,因此需要进行一些研究。

大约 6 个月以来,我一直在努力获得异步 Twisted 思维方式,我终于开始“明白”了。这需要一段时间,但这是一次值得的旅程。

对某些人来说,学习扭曲就像 80 年代基于文本的老式冒险游戏之一,您或多或少地在实验中跌跌撞撞,直到您发现一个吸引有用设备的魔术袋。到那时,事情就变得容易了。

我发现研究扭曲的源代码信息量很大,但其中有很多,知道从哪里开始或找到东西可能具有挑战性。代码往往组织良好,清晰且或多或少一致,这很有帮助。

另一个积极的方面是,与 Twisted 密切相关的人(Glyph 和 JP 只是其中的两个)可以在这里提供帮助。

如果时间允许,我计划将一组示例服务器和客户端放在一起,这些示例服务器和客户端比当前可用的示例执行更多的扭曲的花里胡哨。我希望在这样做之后,我可以让 Twisted 人员审查它们,然后他们可能会考虑将它们提供给其他人。

祝你的 Twisted 旅程好运

于 2014-05-18T03:25:31.953 回答