为什么在字典中存储SFTP对象时Paramiko会引发EOFError()?
我在写一个应用程序时遇到了一些麻烦,这个程序是用来通过SSH下载和上传文件的。现在的问题是,我可以顺利下载文件,但当我尝试把文件上传到另一个服务器时,就会出现EOFError()的错误。我查看了paramiko\sftp.py中的_write_all()函数,发现这个错误似乎是因为它无法将数据写入流中?我对网络编程没有经验,所以如果有人能告诉我它到底在做什么,我会非常感激。
我写了一个简化版的函数来处理我的连接,ssh().runCommand()展示了我的应用程序中上传失败的情况,而simpleTest()则展示了sftp put是如何成功的,但我看不出runCommand()和simpleTest()之间有什么不同,除了我的SFTP对象存储方式不同。一个是存储在字典里,另一个是单独存储。如果字典是问题所在,那下载文件应该也会失败,但事实并非如此。
有没有人知道可能导致这种情况的原因,或者能推荐另一种管理连接的方式,如果现在的方式有问题的话?
我使用的是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
3 个回答
在看了你的修改后,我觉得问题出在这里
stdin, stdout, stderr = self.sshConnections[host].exec_command(command)
这一行显然是断开了FTP的连接
已编辑
我理解的是,当你用ssh=SSHClient()这行代码时,你是在创建一个SSHClient对象。接着,使用sftp=ssh.open_sftp()这行代码,你又创建了一个sftp对象。虽然你只想用sftp,但你把ssh存储在一个本地变量里,这个变量之后会被垃圾回收(gc)。但是,如果ssh被垃圾回收了,sftp就会神奇地停止工作。我也不知道为什么,但建议你在sftp存在的时候,把ssh保留着。
我解决了我的问题。其实我应该使用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)