2012-12-17 28 views
5

ट्विस्टेड शंख एसएसएच कनेक्शन को बंद करने का सही तरीका क्या है? क्या ऐसा करने का कोई स्पष्ट तरीका है?ट्विस्टेड शंख एसएसएच कनेक्शन को बंद करने का सही तरीका क्या है?

सभी ट्विस्ट किए गए शंकु उदाहरण मैंने एसएसएच चैनल को बंद कर देखा है और फिर रिएक्टर को रोक दिया है। रिएक्टर शट डाउन कनेक्शन को बंद करने के लिए प्रतीत होता है। हालांकि, मैं wxPython के साथ wxreactor का उपयोग कर रहा हूं और मैं रिएक्टर को रोकना नहीं चाहता, लेकिन जब मैं इसके साथ समाप्त हो जाता हूं तो मैं एसएसएच कनेक्शन बंद करना चाहता हूं।

t.c.s.connection को देखने के बाद यह सेवा की तरह लग रहा था() विधि जाने का तरीका था। यह सभी खुले चैनलों बंद कर देता है और चलाता है _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 में से किसी को लगता है कि मुझे सेवा कॉल नहीं करना चाहिए() खुद को क्योंकि इसे ट्विस्ट के एक अलग हिस्से द्वारा स्वचालित रूप से बुलाया जाना चाहिए।

मैंने कुछ ट्विस्ट स्रोत कोड में चारों ओर पोकिंग किया और पाया कि सेवास्टॉप को t.c.s.t.SSHClientTransport.connectionLost() द्वारा बुलाया जाना चाहिए।

मैं अपने एसएफटीपी क्लाइंट ऑब्जेक्ट्स का ट्रैक रख रहा हूं और एसएसएच कनेक्शन को उनके परिवहन गुण के माध्यम से एक्सेस कर रहा हूं। यहां एक उदाहरण दिया गया है जिसे आप इस मुद्दे को प्रदर्शित करने के लिए स्थानीय रूप से चला सकते हैं। कच्चे को 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 में एक स्थगित सेट था, लेकिन किसी बिंदु पर यह किसी के लिए सेट नहीं है।

मुझे संदेह है कि SSHClientTransport.loseConnection() एसएसएच कनेक्शन को बंद करने का सही तरीका है, लेकिन क्यों self.factory.d किसी और को सेट नहीं किया जाता है जब यह कुछ और होने की अपेक्षा करता है?

यदि हार कनेक्शन कनेक्शन को बंद करने का सही तरीका नहीं है तो क्या कोई मुझे सही दिशा में इंगित कर सकता है?

उत्तर

4

ऐसा लगता है कि आप twisted.conch.client.direct.SSHClientFactory और twisted.conch.client.direct.SSHClientTransport का उपयोग कर रहे हैं। conch कमांड लाइन उपकरण को लागू करने के लिए इन कक्षाओं का उपयोग सबसे अधिक सीधे किया जाता है। इसका मतलब है कि वे एक एसएसएच क्लाइंट बनाने के लिए काफी उपयोगी हैं, क्योंकि यह बिल्कुल ठीक है conch है।

हालांकि, वे शायद कुछ हद तक कम उपयोगी हो सकते हैं, क्योंकि वे conch कमांड लाइन उपकरण को लागू करने के बजाय कुछ भी 'अन्य' करने के लिए अधिक ध्यान नहीं देते हैं।

अधिक आम तौर पर लागू एसएसएच क्लाइंट ट्रांसपोर्ट क्लास twisted.conch.ssh.transport.SSHClientTransport है। इस श्रेणी में conch कमांड लाइन उपकरण के कुछ विशेष व्यवहार को लागू करने के लिए कोई अतिरिक्त तर्क नहीं है। इसमें सिर्फ एसएसएच क्लाइंट तर्क है। उदाहरण के लिए, इसमें sendDisconnect के अंदर एक अस्पष्ट self.factory.d चेक नहीं है - इसका sendDisconnect कार्यान्वयन केवल डिस्कनेक्ट पैकेट भेजता है और फिर कनेक्शन बंद कर देता है।

1

मैं एक ही समस्या में भागता हूं। मुझे इसकी एक बग आश्वस्त है कि sendDisconnect() पेरेंट कार्यान्वयन को कॉल नहीं करता है। पर पर कॉलिंग मेरे लिए टीसीपी कनेक्शन बंद नहीं करता है, जिसे मैं lsof -p PID का उपयोग करके देख सकता हूं। इस समस्या को ठीक करने के लिए मैं SSHClientTransport के अपने कार्यान्वयन को इंजेक्ट करने के लिए अपने स्वयं के connect() विधि का उपयोग करता हूं। समस्या निम्न कोड के साथ तय की गई है:

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))