在reactor.run()后向Twisted SSH发送命令的可靠方法
大家好,这是一个关于Python的Twisted SSH库的问题。
我看到的所有示例代码和生产代码,作为基于twisted.conch.ssh的SSH客户端,都是以这样的方式与服务器互动的:
- 准备一些要远程执行的命令;
- 定义一些回调函数;
- 启动反应器,然后暂停等待新的反馈;
在调用reactor.run()之后,我从没见过有人尝试向sshd发送命令,脚本只是静静地等待。我觉得可能可以通过分叉或生成新的进程来发送命令。不过,由于Twisted的一个优势是它的多路复用机制,所以在作为服务器运行时,它不需要分叉来处理传入的请求。我可以说,作为客户端脚本,不分叉而持续发送请求到服务器是一个合理的需求吗?
对此有什么想法吗?
谢谢!
2 个回答
你就像是在试图把一个方形的木头塞进一个圆形的洞里。在Twisted这个框架里,一切都是异步的,所以你需要以不同的方式来考虑事件的顺序。你不能简单地说“这里有10个操作要一个接一个地执行”,那样的想法叫做串行思维。
在Twisted中,你先发出第一个命令,然后注册一个回调函数,这个函数会在第一个命令完成时被触发。当这个回调被触发时,你再发出第二个命令,并注册一个回调函数,这个函数会在第二个命令完成时被触发。就这样,一步一步来。
joefis的回答基本上是正确的,不过我觉得加一些例子会更有帮助。首先,有几种方法可以让一些代码在反应器启动后立即运行。
第一种方法很简单:
def f():
print "the reactor is running now"
reactor.callWhenRunning(f)
另一种方法是使用定时事件,虽然其实没必要这样做,直接用callWhenRunning
就可以了:
reactor.callLater(0, f)
你也可以使用底层的API,callWhenRunning
就是基于这个API实现的:
reactor.addSystemEventTrigger('after', 'startup', f)
你还可以使用服务。这种方法稍微复杂一点,因为它需要用到twistd(1)
(或者其他能把服务系统连接到反应器的东西)。不过你可以写一个这样的类:
from twisted.application.service import Service
class ThingDoer(Service):
def startService(self):
print "The reactor is running now."
然后写一个.tac文件,内容可以是这样的:
from twisted.application.service import Application
from thatmodule import ThingDoer
application = Application("Do Things")
ThingDoer().setServiceParent(application)
最后,你可以用twistd(1)
来运行这个.tac文件:
$ twistd -ny thatfile.tac
当然,这只告诉你如何在反应器运行后做一件事,这并不是你问的全部内容。不过思路是一样的——你定义一个事件处理器,并请求通过调用这个处理器来接收一个事件;当这个处理器被调用时,你就可以执行一些操作。这个思路同样适用于你在Conch中做的任何事情。
你可以在Conch示例中看到这一点,比如在sshsimpleclient.py中,我们有:
class CatChannel(channel.SSHChannel):
name = 'session'
def openFailed(self, reason):
print 'echo failed', reason
def channelOpen(self, ignoredData):
self.data = ''
d = self.conn.sendRequest(self, 'exec', common.NS('cat'), wantReply = 1)
d.addCallback(self._cbRequest)
def _cbRequest(self, ignored):
self.write('hello conch\n')
self.conn.sendEOF(self)
def dataReceived(self, data):
self.data += data
def closed(self):
print 'got data from cat: %s' % repr(self.data)
self.loseConnection()
reactor.stop()
在这个例子中,channelOpen
是当一个新通道打开时被调用的事件处理器。它会向服务器发送请求。然后它会得到一个Deferred
,并为它附加一个回调。这个回调是一个事件处理器,当请求成功时(在这个例子中,就是当cat
被执行时)会被调用。_cbRequest
就是它附加的回调,这个方法会执行下一步——向通道写入一些字节,然后关闭它。接着是dataReceived
事件处理器,它在通道收到字节时被调用,还有closed
事件处理器,在通道关闭时被调用。
所以你可以看到这里有四个不同的事件处理器,其中一些是启动操作,最终会触发后面的事件处理器。
回到你关于一个接一个做事情的问题,如果你想依次打开两个cat通道,那么在closed
事件处理器中可以打开一个新通道(而不是像这个例子中那样停止反应器)。