Jaki jest prawidłowy sposób zamknięcia połączenia konwergentnego konektora SSH? Czy istnieje wyraźny sposób na zrobienie tego?Jaki jest prawidłowy sposób zamknięcia połączenia konwergentnego konektora SSH?
Wszystkie skręcone przykłady konchy Widziałem blisko kanału SSH, a następnie zatrzymałem reaktor. Zamykanie reaktora wydaje się obsługiwać zamykanie połączenia. Jednak używam wxreactor z wxPython i nie chcę zatrzymywać reaktora, ale chcę zamknąć połączenie ssh, gdy skończę z nim.
Po obejrzeniu połączenia t.c.s. wydaje się, że metoda serviceStopped() była drogą. To zamyka wszystkie otwarte kanały i biegnie _cleanupGlobalDeferreds() po zakończeniu, ale potem zaczęła się wyjątki jak poniżej:
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
Wygląda jakbym nadal pobieranie danych z serwera po kanał został zamknięty. Ktoś z #twisted wydawał się myśleć, że nie powinienem wywoływać serviceStopped() samemu, ponieważ powinien być wywoływany automatycznie przez inną część Twisted.
Zrobiłem kilka szturchanie w Twisted kod źródłowy i stwierdził, że serviceStopped ma być wywoływane przez t.c.s.t.SSHClientTransport.connectionLost().
Śledzę moje obiekty klienta SFTP i uzyskuję dostęp do połączenia SSH za pośrednictwem ich atrybutu transportu. Oto przykład, który możesz uruchomić lokalnie, aby zademonstrować problem. Surowiec można pobrać 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()
Aby zamknąć połączenie SSH, dzwonię ACTIVE_CLIENTS[HOST].transport.conn.transport(t.c.c.d.SSHClientTransport instance).loseConnection()
który wywołuje t.c.c.d.SSHClientTransport.sendDisconnect()
. Oto metoda 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 wydaje się być zawsze None, gdy ta metoda nazywa się tak zwraca bez wzywania t.c.s.t.SSHClientTransport.sendDisconnect(). Myślę, że pierwotnie był to odroczony zestaw w t.c.c.d.connect, ale w pewnym momencie jest ustawiony na None.
Podejrzewam, że SSHClientTransport.loseConnection() jest poprawnym sposobem zamknięcia połączeń SSH, ale dlaczego self.factory.d ma wartość None, gdy twisted oczekuje, że będzie to coś innego?
Jeśli metoda loseConnection() nie jest poprawną metodą zamykania połączeń SSH, czy ktoś mógłby wskazać mi właściwy kierunek?