什麼是關閉扭曲海螺SSH連接的正確方法?有沒有明確的方法來做到這一點?什麼是關閉雙絞海螺SSH連接的正確方法?
我看到的所有Twisted海螺的例子都關閉了SSH通道,然後停止反應堆。反應堆停堆似乎處理關閉連接。然而,我使用wxPython的wxreactor,並且我不想停止反應堆,但是當我完成它時,我想關閉ssh連接。
查看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源代碼中做了一些動作,發現serviceStopped應該由t.c.s.t.SSHClientTransport.connectionLost()調用。
我跟蹤我的SFTP客戶端對象並通過其傳輸屬性訪問SSH連接。以下是您可以在本地運行以演示此問題的示例。原料可以取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連接,我打電話ACTIVE_CLIENTS[HOST].transport.conn.transport(t.c.c.d.SSHClientTransport instance).loseConnection()
這就要求t.c.c.d.SSHClientTransport.sendDisconnect()
。下面是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似乎永遠是無時,調用該方法,因此返回不調用t.c.s.t.SSHClientTransport.sendDisconnect()。我認爲它最初是t.c.c.d.connect中的延遲集,但是在某些時候它被設置爲None。
我懷疑SSHClientTransport.loseConnection()是關閉SSH連接的正確方法,但爲什麼當twisted被期望成爲別的東西時,self.factory.d設置爲None?
如果loseConnection()不是關閉SSH連接的正確方法,有人可能會指向正確的方向嗎?