twisted - 以交互方式排列函数

时间:2014-05-17 17:05:02

标签: python asynchronous twisted reactor

我刚刚被介绍通过this nice tutorial加入聊天应用程序,但我不确定如何根据我的需求进行调整。

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

我想做这样的事情:

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线程,但它没有用。我看了these twisted examples,但他们最后都有reactor.run()语句,这让我失望了。是的,我很可能错过了一些基本的东西(这就是我在这里的原因)。

3 个答案:

答案 0 :(得分:2)

您不需要更改运行反应堆的方式来实现此行为。

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

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

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

任何时候你认为自己采取行动并且#34;想一想为什么它在采取行动 - 在什么条件下或在什么情况下做出反应 - 并且你将要找出它实际上正在做出反应的事件

答案 1 :(得分:1)

正如JP所说,反应堆回应了一个事件。 假设你在Linux / Unix系统上运行:

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

我已经努力获得一个异步的Twisted心态大约6个月了,我终于开始“搞定了”。这需要一段时间,但这是一次有价值的旅程。

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

我发现研究扭曲的源代码非常有用,但是它有很多,知道从哪里开始或找到东西可能很有挑战性。代码往往组织良好,清晰,或多或少一致,这有助于。

另一个积极因素是那些非常参与Twisted(Glyph和JP只是其中两个)的人可以在这里提供帮助。

如果时间允许,我计划将一组示例服务器和客户端放在一起,这些服务器和客户端比现有的示例更多地使用扭曲的花哨。我希望在这样做之后,我可以让Twisted人员对他们进行评估,然后他们可能会考虑将其提供给其他人。

祝你扭曲的旅程好运

答案 2 :(得分:1)

你说过:

  

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

  

我想我想要做的就是在我坐在服务器上时用Python交互式生成事件。

我要将其转换为" 我希望在我的反应堆运行时有一个键盘界面"并举例说明。

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

(Disclamer:这个例子有点乱,因为它在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连接并等待密钥在stdin上发生的反应堆。键入密钥'将向所有连接的客户发送消息。

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