关闭扭曲海螺SSH连接的正确方法是什么?有没有明确的方法来做到这一点?
我见过的所有扭曲海螺的例子都关闭了SSH通道,然后停止了反应堆。反应堆关闭似乎解决了连接的关闭问题。但是,我正在使用带有wxPython的wxreactor,并且我不想停止该反应器,但我想在使用完它后关闭它。
看过t.c.s.connection之后,似乎应该使用serviceStopped()方法。它关闭所有打开的通道,并在完成时运行_cleanupGlobalDeferreds(),但随后我开始收到如下所示的异常:
Unhandled Error
Traceback (most recent call last):
File "C:\Users\me\venv\lib\site-packages\twisted\internet\tcp.py", line 203, in doRead
return self._dataReceived(data)
File "C:\Users\me\venv\lib\site-packages\twisted\internet\tcp.py", line 209, in _dataReceived
rval = self.protocol.dataReceived(data)
File "C:\Users\me\venv\lib\site-packages\twisted\conch\ssh\transport.py", line 438, in dataReceived
self.dispatchMessage(messageNum, packet[1:])
File "C:\Users\me\venv\lib\site-packages\twisted\conch\ssh\transport.py", line 460, in dispatchMessage
messageNum, payload)
--- <exception caught here> ---
File "C:\Users\me\venv\lib\site-packages\twisted\python\log.py", line 84, in callWithLogger
return callWithContext({"system": lp}, func, *args, **kw)
File "C:\Users\me\venv\lib\site-packages\twisted\python\log.py", line 69, in callWithContext
return context.call({ILogContext: newCtx}, func, *args, **kw)
File "C:\Users\me\venv\lib\site-packages\twisted\python\context.py", line 118, in callWithContext
return self.currentContext().callWithContext(ctx, func, *args, **kw)
File "C:\Users\me\venv\lib\site-packages\twisted\python\context.py", line 81, in callWithContext
return func(*args,**kw)
File "C:\Users\me\venv\lib\site-packages\twisted\conch\ssh\service.py", line 44, in packetReceived
return f(packet)
File "C:\Users\me\venv\lib\site-packages\twisted\conch\ssh\connection.py", line 228, in ssh_CHANNEL_DATA
channel = self.channels[localChannel]
exceptions.KeyError: 0看起来在通道关闭后,我仍然可以从服务器获取数据。#twisted中的某个人似乎认为我不应该自己调用serviceStopped(),因为它应该由Twisted的不同部分自动调用。
我仔细查看了Twisted源代码,发现t.c.s.t.SSHClientTransport.connectionLost()应该调用serviceStopped。
我正在跟踪我的SFTP客户机对象,并通过它们的传输属性访问SSH连接。这里有一个你可以在本地运行的例子来演示这个问题。raw可以通过here来获取。
from os.path import basename
import sys
from twisted.conch.client.connect import connect
from twisted.conch.client.options import ConchOptions
from twisted.internet.defer import Deferred
from twisted.conch.ssh import channel, userauth
from twisted.conch.ssh.common import NS
from twisted.conch.ssh.connection import SSHConnection
from twisted.conch.ssh.filetransfer import FXF_WRITE, FXF_CREAT, \
FXF_TRUNC, FileTransferClient
from twisted.internet import reactor, defer
from twisted.python.log import startLogging
ACTIVE_CLIENTS = {}
USERNAME = 'user' # change me!
PASSWORD = 'password' # change me!
HOST = ('hostname', 22) # change me!
TEST_FILE_PATH = __file__
TEST_FILE_NAME = basename(__file__)
def openSFTP(user, host):
conn = SFTPConnection()
options = ConchOptions()
options['host'], options['port'] = host
conn._sftp = Deferred()
auth = SimpleUserAuth(user, conn)
connect(options['host'], options['port'], options, verifyHostKey, auth)
return conn._sftp
def verifyHostKey(ui, hostname, ip, key):
return defer.succeed(True)
class SimpleUserAuth(userauth.SSHUserAuthClient):
def getPassword(self):
return defer.succeed(PASSWORD)
class SFTPConnection(SSHConnection):
def serviceStarted(self):
self.openChannel(SFTPChannel())
class SFTPChannel(channel.SSHChannel):
name = 'session'
def channelOpen(self, ignoredData):
d = self.conn.sendRequest(self, 'subsystem', NS('sftp'),
wantReply=True)
d.addCallback(self._cbFTP)
d.addErrback(self.printErr)
def _cbFTP(self, ignore):
client = FileTransferClient()
client.makeConnection(self)
self.dataReceived = client.dataReceived
ACTIVE_CLIENTS.update({self.conn.transport.transport.addr: client})
self.conn._sftp.callback(None)
def printErr(self, msg):
print msg
return msg
@defer.inlineCallbacks
def main():
d = openSFTP(USERNAME, HOST)
_ = yield d
client = ACTIVE_CLIENTS[HOST]
d = client.openFile(TEST_FILE_NAME, FXF_WRITE | FXF_CREAT | FXF_TRUNC, {})
df = yield d
sf = open(TEST_FILE_PATH, 'rb')
d = df.writeChunk(0, sf.read())
_ = yield d
sf.close()
d = df.close()
_ = yield d
ACTIVE_CLIENTS[HOST].transport.loseConnection()
# loseConnection() call above causes the following log messages:
# [SSHChannel session (0) on SSHService ssh-connection on SSHClientTransport,client] sending close 0
# [SSHChannel session (0) on SSHService ssh-connection on SSHClientTransport,client] unhandled request for exit-status
# [SSHChannel session (0) on SSHService ssh-connection on SSHClientTransport,client] remote close
# [SSHChannel session (0) on SSHService ssh-connection on SSHClientTransport,client] closed
# I can see the channel closed on the server side:
# sshd[4485]: debug1: session_exit_message: session 0 channel 0 pid 4486
# sshd[4485]: debug1: session_exit_message: release channel 0
# sshd[4485]: debug1: session_by_channel: session 0 channel 0
ACTIVE_CLIENTS[HOST].transport.conn.transport.loseConnection()
# loseConnection() call above does not close the SSH connection.
reactor.callLater(5, reactor.stop)
# Stopping the reactor closes the SSH connection and logs the following messages:
# [SSHClientTransport,client] connection lost
# [SSHClientTransport,client] Stopping factory <twisted.conch.client.direct.SSHClientFactory instance at 0x02E5AF30>
# [-] Main loop terminated.
# On the server side:
# sshd[4485]: Closing connection to xxx.xxx.xxx.xxx
if __name__ == '__main__':
startLogging(sys.stdout)
reactor.callWhenRunning(main)
reactor.run()为了关闭SSH连接,我调用了调用t.c.c.d.SSHClientTransport.sendDisconnect()的ACTIVE_CLIENTS[HOST].transport.conn.transport(t.c.c.d.SSHClientTransport instance).loseConnection()。下面是sendDisconnect()方法:
def sendDisconnect(self, code, reason):
if self.factory.d is None:
return
d, self.factory.d = self.factory.d, None
transport.SSHClientTransport.sendDisconnect(self, code, reason)
d.errback(error.ConchError(reason, code))当调用此方法时,self.factory.d似乎始终为None,因此它返回时不会调用t.c.s.t.SSHClientTransport.sendDisconnect()。我认为它最初是t.c.c.d.connect中的一个延迟集,但在某种程度上它被设置为None。
我怀疑SSHClientTransport.loseConnection()是关闭SSH连接的正确方法,但是为什么self.factory.d设置为None,而twisted希望它是其他的呢?
如果loseConnection()不是关闭SSH连接的正确方法,有人能告诉我正确的方向吗?
发布于 2013-01-10 05:06:34
听起来您使用的是twisted.conch.client.direct.SSHClientFactory和twisted.conch.client.direct.SSHClientTransport。这些类最直接用于实现conch命令行工具。这意味着它们对于构建SSH客户端相当有用,因为这正是conch。
然而,它们通常也没有人们想象的那么有用,因为它们除了实现conch命令行工具之外,并没有太多的注意力去做任何“其他”的事情。
更普遍适用的SSH客户端传输类是twisted.conch.ssh.transport.SSHClientTransport。这个类没有任何额外的逻辑来实现conch命令行工具的某些特定行为。它只有SSH客户端逻辑。例如,它在sendDisconnect中没有一个无法解释的self.factory.d检查--它的sendDisconnect实现只是发送一个断开数据包,然后关闭连接。
发布于 2013-05-28 22:03:12
我遇到了同样的问题。我确信这是一个错误,sendDisconnect()不调用父实现。在SSHClientTransport上调用loseConnection()不会为我关闭TCP连接,我可以使用lsof -p PID看到这一点。为了解决这个问题,我使用我自己的connect()方法来注入我自己的SSHClientTransport实现。该问题已通过以下代码修复:
class SSHClientTransport(direct.SSHClientTransport):
'''
Orignal sendDisconnect() is bugged.
'''
def sendDisconnect(self, code, reason):
d, self.factory.d = self.factory.d, None
# call the sendDisconnect() on the base SSHTransport,
# not the imediate parent class
transport.SSHClientTransport.sendDisconnect(self, code, reason)
if d:
d.errback(error.ConchError(reason, code))https://stackoverflow.com/questions/13920962
复制相似问题