【问题标题】:Why is Paramiko raising EOFError() when the SFTP object is stored in a dictionary?当 SFTP 对象存储在字典中时,为什么 Paramiko 会引发 EOFError()?
【发布时间】:2011-03-17 16:47:08
【问题描述】:

我正在编写的应用程序通过 SSH 将文件下载到其他机器或从其他机器上传文件时遇到问题。我遇到的问题是我可以很好地获取(下载)文件,但是当我尝试将它们放到(上传)到另一台服务器上时,我得到了 EOFError() 异常。当我查看 paramiko\sftp.py 中的 _write_all() 时,似乎错误是因为它无法将任何数据写入流?我没有网络编程经验,所以如果有人知道它要做什么并且可以与我交流,我将不胜感激。

我编写了一个简化版本的函数,将我的连接处理为 ssh()。 runCommand() 显示上传在我的应用程序中是如何失败的,而 simpleTest() 显示 sftp put 是如何工作的,但是除了我的 SFTP 对象的存储方式之外,我看不出 runCommand() 和 simpleTest() 之间的任何区别。一个存储在字典中,另一个单独存储。似乎字典是无法下载文件的问题,但事实并非如此。

有谁知道什么可能导致这种行为,或者如果这种方式导致问题,是否可以推荐另一种方式来管理我的连接?

我正在使用 Python 2.7 和 Paramiko 1.7.6。我已经在 Linux 和 Windows 上测试了这段代码,得到了相同的结果。

编辑:现在包含代码。

import os
import paramiko

class ManageSSH:
     """Manages ssh connections."""
    def __init__(self):
        self.hosts = {"testbox": ['testbox', 'test', 'test']}
        self.sshConnections = {}
        self.sftpConnections = {}
        self.localfile = "C:\\testfile"
        self.remotefile = "/tmp/tempfile"
        self.fetchedfile = "C:\\tempdl"

    def ssh(self):
        """Manages ssh connections."""
        for host in self.hosts.keys():
            try:
                self.sshConnections[host]
                print "ssh connection is already open for %s" % host
            except KeyError, e:         # if no ssh connection for the host exists then open one
                # open ssh connection
                ssh = paramiko.SSHClient()
                ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                ssh.connect(self.hosts[host][0], 22, self.hosts[host][1], self.hosts[host][2])
                self.sshConnections[host] = ssh
                print "ssh connection to %s opened" % host
            try:
                self.sftpConnections[host]
                print "sftp connection is already open for %s" % host
            except KeyError, e:
                # open sftp connection
                ssh = paramiko.SSHClient()
                ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                ssh.connect(self.hosts[host][0], 22, self.hosts[host][1], self.hosts[host][2])
                self.sftpConnections[host] = ssh.open_sftp()
                print "sftp connection to %s opened" % host

    def runCommand(self):
        """run commands and return output"""
        for host in self.hosts:
            command = "if [ -d /tmp ]; then echo -n 1; else echo -n 0; fi"
            stdin, stdout, stderr = self.sshConnections[host].exec_command(command)
            print "%s executed on %s" % (command, host)
            print "returned %s" % stdout.read()
            self.sftpConnections.get(self.remotefile, self.fetchedfile)
            print "downloaded %s from %s" % (self.remotefile, host)
            self.sftpConnections[host].put(self.localfile, self.remotefile)
            print "uploaded %s to %s" % (self.localfile, host)
            self.sftpConnections[host].close()
            self.sshConnections[host].close()

    def simpleTest(self):
        host = "testbox"
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(host, 22, 'test', 'test')
        sftp = ssh.open_sftp()
        print "sftp connection to %s opened" % host
        sftp.get(self.remotefile, self.fetchedfile)
        print "downloaded %s from %s" % (self.localfile, host)
        sftp.put(self.localfile, self.remotefile)
        print "uploaded %s to %s" % (self.localfile, host)
        sftp.close()

if __name__ == "__main__":
    test = ManageSSH()
    print "running test that works"
    test.simpleTest()
    print "running test that fails"
    test.ssh()
    test.runCommand()

输出:

running test that works
sftp connection to testbox opened
downloaded C:\testfile from testbox
uploaded C:\testfile to testbox
running test that fails
ssh connection to testbox opened
sftp connection to testbox opened
if [ -d /tmp ]; then echo -n 1; else echo -n 0; fi executed on testbox
returned 1
downloaded /tmp/tempfile from testbox
Traceback (most recent call last):
  File "paramikotest.py", line 71, in <module>
    test.runCommand()
  File "paramikotest.py", line 47, in runCommand
    self.sftpConnections[host].put(self.localfile, self.remotefile)
  File "C:\Python27\lib\site-packages\paramiko\sftp_client.py", line 561, in put

    fr = self.file(remotepath, 'wb')
  File "C:\Python27\lib\site-packages\paramiko\sftp_client.py", line 245, in open
    t, msg = self._request(CMD_OPEN, filename, imode, attrblock)
  File "C:\Python27\lib\site-packages\paramiko\sftp_client.py", line 627, in _request
    num = self._async_request(type(None), t, *arg)
  File "C:\Python27\lib\site-packages\paramiko\sftp_client.py", line 649, in _async_request
    self._send_packet(t, str(msg))
  File "C:\Python27\lib\site-packages\paramiko\sftp.py", line 172, in _send_packet
    self._write_all(out)
  File "C:\Python27\lib\site-packages\paramiko\sftp.py", line 138, in _write_all

    raise EOFError()
EOFError

【问题讨论】:

    标签: python ssh sftp paramiko


    【解决方案1】:

    我能够解决我的问题。我应该使用 Paramiko.Transport,然后使用paramiko.SFTPClient.from_transport(t) 创建SFTPClient,而不是使用来自SSHClient()open_sftp()

    以下代码有效:

    t = paramiko.Transport((host, 22))  
    t.connect(username=username, password=password)  
    sftp = paramiko.SFTPClient.from_transport(t)
    

    【讨论】:

    • 但是,使用相同的代码,这个异常是随机引发的
    【解决方案2】:

    在我看来,使用 ssh=SSHClient() 创建一个 SSHClient-Object,然后使用 sftp=ssh.open_sftp() 创建一个 sftp-object。虽然您只想使用 sftp,但您将 ssh 存储在一个局部变量中,然后该变量会被 gc'd,但是,如果 ssh 是 gc'd,则 sftp 会神奇地停止工作。不知道为什么,但是尝试在您的 sftp 存在的时间内存储 ssh。

    【讨论】:

      【解决方案3】:

      阅读您的编辑后,我认为问题出在这里

      stdin, stdout, stderr = self.sshConnections[host].exec_command(command)
      

      这条线显然断开了 ftp 的东西

      已编辑

      【讨论】:

      • 评论移到下面的答案。
      最近更新 更多