วิธีที่ถูกต้องในการปิดการเชื่อมต่อ Twisted conch SSH คืออะไร

วิธีที่ถูกต้องในการปิดการเชื่อมต่อ Twisted conch SSH คืออะไร มีวิธีที่ชัดเจนในการทำเช่นนี้หรือไม่?

ตัวอย่างหอยสังข์บิดทั้งหมดที่ฉันเห็นปิดช่อง SSH แล้วหยุดเครื่องปฏิกรณ์ การปิดเครื่องปฏิกรณ์ดูเหมือนว่าจะจัดการกับการปิดการเชื่อมต่อ อย่างไรก็ตาม ฉันใช้ wxreactor กับ wxPython และฉันไม่ต้องการหยุดเครื่องปฏิกรณ์ แต่ฉันต้องการปิดการเชื่อมต่อ ssh เมื่อใช้งานเสร็จแล้ว

หลังจากดูการเชื่อมต่อ t.c.s. แล้ว ดูเหมือนว่าเมธอด 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.SHSClientTransport.connectionLost()

ฉันกำลังติดตามออบเจ็กต์ไคลเอนต์ SFTP ของฉันและเข้าถึงการเชื่อมต่อ SSH ผ่านแอตทริบิวต์การขนส่ง นี่คือตัวอย่างที่คุณสามารถเรียกใช้ในเครื่องเพื่อแสดงปัญหาได้ สามารถดึงข้อมูลดิบได้ที่นี่

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 ดูเหมือนจะเป็น None เสมอเมื่อมีการเรียกใช้เมธอดนี้ ดังนั้นจึงส่งคืนโดยไม่ต้องเรียก t.c.s.t.SHSClientTransport.sendDisconnect() ฉันคิดว่าเดิมทีมันเป็นชุดที่เลื่อนออกไปใน t.c.c.d.connect แต่เมื่อถึงจุดหนึ่งก็ตั้งเป็นไม่มี

ฉันสงสัยว่า SSHClientTransport.loseConnection() เป็นวิธีที่ถูกต้องในการปิดการเชื่อมต่อ SSH แต่เหตุใด self.factory.d จึงตั้งค่าเป็น None เมื่อ twisted คาดว่าจะเป็นอย่างอื่น

หาก lossConnection() ไม่ใช่วิธีที่ถูกต้องในการปิดการเชื่อมต่อ SSH มีใครช่วยชี้ทิศทางที่ถูกต้องให้ฉันได้ไหม


person Vye    schedule 17.12.2012    source แหล่งที่มา


คำตอบ (2)


ดูเหมือนว่าคุณกำลังใช้ twisted.conch.client.direct.SSHClientFactory และ twisted.conch.client.direct.SSHClientTransport คลาสเหล่านี้มีวัตถุประสงค์โดยตรงที่สุดเพื่อใช้ใช้เครื่องมือบรรทัดคำสั่ง conch ซึ่งหมายความว่าค่อนข้างมีประโยชน์สำหรับการสร้างไคลเอ็นต์ SSH เนื่องจากนั่นคือสิ่งที่ conch เป็นอย่างแท้จริง

อย่างไรก็ตาม โดยทั่วไปแล้ว สิ่งเหล่านี้ยังมีประโยชน์น้อยกว่าที่คิด เนื่องจากพวกเขาไม่ได้ใส่ใจกับการทำอะไร ''อื่น'' มากไปกว่าการใช้เครื่องมือบรรทัดคำสั่ง conch

คลาสการขนส่งไคลเอ็นต์ SSH ที่เกี่ยวข้องโดยทั่วไปมากกว่าคือ twisted.conch.ssh.transport.SSHClientTransport คลาสนี้ไม่มีตรรกะพิเศษสำหรับการนำลักษณะการทำงานบางอย่างของเครื่องมือบรรทัดคำสั่ง conch ไปใช้งาน มันแค่มีตรรกะไคลเอนต์ SSH ตัวอย่างเช่น ไม่มีการตรวจสอบ self.factory.d ที่ไม่สามารถอธิบายได้ภายใน sendDisconnect - การใช้งาน sendDisconnect ของมันเพียงแค่ส่งแพ็คเก็ตการยกเลิกการเชื่อมต่อแล้วปิดการเชื่อมต่อ

person Jean-Paul Calderone    schedule 09.01.2013

ฉันประสบปัญหาเดียวกัน ฉันเชื่อว่ามันเป็นข้อบกพร่องที่ sendDisconnect() ไม่เรียกว่าการใช้งานหลัก การโทร loseConnection() บน SSHClientTransport ไม่ได้ปิดการเชื่อมต่อ 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))
person Marek Kowalski    schedule 28.05.2013