Net::SFTP连接未关闭

7

我可以帮助您翻译以下这段关于IT技术的内容,涉及Ruby on Rails(Rails 3.2.14 和 ruby 1.9.3)应用程序上传 2 个文件到远程 SFTP 服务器的代码如下:

require 'net/sftp'
Rails.logger.info("Creating SFTP connection")
uri = URI.parse('sftp://'+ host)
Net::SFTP.start(uri.host,'user', :password=>'password',:port=>port) do |sftp|
    Rails.logger.info("SFTP Connection created, uploading files.")
    sftp.upload!("public/file1.txt", "./file1.txt")
    Rails.logger.info("First file uploaded.")
    sftp.upload!("file2.txt", "./file2.txt")
    Rails.logger.info("Both files uploaded, terminating connection.")
end
Rails.logger.info("Connection terminated.")

两个文件都成功上传到远程服务器,但连接似乎没有关闭。当我执行这个函数时,一直出现错误,分析控制台后发现,“两个文件上传成功,终止连接。”的日志信息被记录了,但之后没有任何输出。我尝试使用

sftp.close(:handle)
sftp.close!(:handle)
#and
sftp.close_connection()

但是它们都没有起作用。有什么想法为什么会发生这种情况以及我如何纠正它?我正在通过单个实例的Engine Yard云服务器运行此项操作。
编辑 这是日志中的最后几行: 创建SFTP连接 SFTP连接已创建,上传文件。 第一个文件已上传。 两个文件已上传,终止连接。
之后就没有了。当使用“tail -f”命令查看我的日志时,日志到达了最后一行,并且应用程序重定向到内部服务器错误页面。

问题不在于连接没有关闭。当代码块结束时,它会自动关闭。你一直遇到的错误是什么? - techvineet
最好查看日志 log/production.log。 - techvineet
你需要将这个内容发布到控制器和动作中。 - techvineet
我不太明白你在说什么。我尝试通过实现异常处理来找出确切的错误,但即使这样也没有成功。无论是什么错误,它都完全跳过了catch块并直接进入了内部服务器错误页面。 - adnann
在你的Rails.root/log/production.log中有日志文件。你可以在那里看到确切的错误。如果不确定,请在此处发布最后几行。 - techvineet
显示剩余3条评论
3个回答

16

简短回答

Net::SFTP.start('host', 'user', password: 'pass', port: 22) do |sftp|

  # Do stuff

end

等同于:

session = Net::SSH.start('host', 'user', password: 'pass', port: 22)
sftp = Net::SFTP::Session.new(session)
sftp.connect!

# Do stuff

sftp.close_channel unless sftp.nil?
session.close unless session.nil?

更好的答案

对于那些找不到实际关闭连接方法而不使用自动关闭块的人,以下是如何达成这个目标的方法:

require 'net/ssh'
require 'net/sftp'

begin

  # Instance SSH/SFTP session :
  session = Net::SSH.start('host', 'user', password: 'pass', port: 22)
  sftp = Net::SFTP::Session.new(session)

  # Always good to timeout :
  Timeout.timeout(10) do
    sftp.connect! # Establish connection

    # Do stuff

  end

rescue Timeout::Error => e

  # Do some custom logging
  puts e.message 

ensure

  # Close SSH/SFTP session
  sftp.close_channel unless sftp.nil? # Close SFTP
  session.close unless session.nil? # Then SSH

  # If you really really really wanna make sure it's closed,
  # and raise after 10 seconds delay
  Timeout.timeout(10) do
    sleep 1 until (sftp.nil? or sftp.closed?) and (session.nil? or session.closed?)
  end

end

如果在执行其他任务之前不关闭连接,例如在Rails中,您可能会遇到类似于以下错误的情况:

IOError (not opened for reading) # Not closed when rendering controller action
ActionView::Template::Error (not opened for reading) # Not closed when rendering template

1
非常感谢这里对Net:SFTP:start结构的分解说明。 - Ethel Evans
1
虽然这是一个非常好的分解。但人们需要被警告关于“超时”及其潜在影响。https://jvns.ca/blog/2015/11/27/why-rubys-timeout-is-dangerous-and-thread-dot-raise-is-terrifying/ - TheRealMrCrowley
1
@TheRealMrCrowley:谢谢,我不知道这个。幸运的是,我在生产中的应用程序中总是使用显式的rescue Timeout::Error :) - Ghis
请注意,您无需手动创建 Net::SSH 会话。如果您正在使用 Net::SFTP.start,您仍然可以使用 sftp.session.close 关闭 SSH 会话(首先执行 sftp.close_channel)。 - nyi

4
自动关闭块将能够在您的块末尾发送EOF时关闭:
Net::SFTP.start(host, user, port: port, password: password) do |sftp|
    # ... do your things
    sftp.channel.eof!
end

我正在与其中一个服务器进行集成,但在一段时间后出现了问题,关闭通道后问题得到解决。不过,我仍然不确定为什么只有一个服务器会出现这个问题。 - lsdr
对我也起作用了,但有人能解释一下为什么吗? - undefined

3

我不知道为什么,但是将代码重写以包含 'net/ssh' 并通过 SSH 显式地创建 sftp 连接就可以了!

require 'net/ssh'
require 'net/sftp'
#upload a file or directory to the remote host
Rails.logger.info("Creating SFTP connection")
session=Net::SSH.start('host','user', :password=>'password',:port=>port)
sftp=Net::SFTP::Session.new(session).connect!
Rails.logger.info("SFTP Connection created, uploading files.")
sftp.upload!("file1.txt", "./file1.txt")
Rails.logger.info("First file uploaded.")
sftp.upload!("file2.txt", "./file2.txt")
Rails.logger.info("Both files uploaded, terminating connection.")
Rails.logger.info("Connection terminated.")

也许有人能够解释一下为什么这个有效而另一个无效。

我遇到了同样的问题,而这个解决办法是唯一能解决它的方法。 - Doug

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接