【问题标题】:How to properly close a Net::SSH connection?如何正确关闭 Net::SSH 连接?
【发布时间】:2023-03-22 06:04:01
【问题描述】:

很多文章都通过使用块来演示Net::SSH,例如:

Net::SSH.start("host", "user") do |ssh|
  ssh.exec! "cp /some/file /another/location"
  hostname = ssh.exec!("hostname")

  ssh.open_channel do |ch|
    ch.exec "sudo -p 'sudo password: ' ls" do |ch, success|
      abort "could not execute sudo ls" unless success

      ch.on_data do |ch, data|
        print data
        if data =~ /sudo password: /
          ch.send_data("password\n")
        end
      end
    end
  end

  ssh.loop
end

但是,我实际上是在 Ruby 类中使​​用它,并从我的应用程序中的各种其他函数和方法调用它。例如,我有一个 SSHCommand 类,它执行以下操作:

class SSHCommand
    def initialize
        ...
        @ssh = establish_ssh
        ...
    end

    def establish_ssh
        ssh = Net::SSH.start(
            @ip, 'root',
            :host_key => 'ssh-rsa',
            :encryption => 'aes256-ctr',
            :keys => [@key],
            :compression => "zlib@openssh.com",
            :port => @port
        )
        return ssh
    end
    def execute(command)
        results = String.new
        results = run_cmd(command)
        if results.include? "no matches found"
            results = ""
        end
        return results
    end
end

要通过 SSH 连接执行命令,我只需运行以下命令:

ssh = SSHCommand.new
ssh.execute("ifconfig")

我如何真正终止这个 SSH 会话?我注意到当我在 Ruby on Rails 中的 Sidekiq 工作人员完成时,我收到以下消息:

zlib(finalizer): the stream was freed prematurely.
zlib(finalizer): the stream was freed prematurely.

我避免使用块的原因是因为我希望通过已经建立的 SSH 连接从我的工作人员发送多个命令。

我没有按照预期的方式使用它,还是在我完成任务后有一种实际的方法可以关闭此连接?

【问题讨论】:

  • 您也可以重写初始化程序以使用块,例如SSHCommand.new { |ssh| ssh.execute("ifconfig") ... }
  • 有没有办法按照它写的方式关闭它?
  • 好吧,你的SSHCommand 班级不知道工人何时完成发送命令,是吗?将工人的代码包装在一个块中是我能想到的最简单的方法。另一种方法是使用ObjectSpace.define_finalizer,但在我看来,一个普通的块要干净得多。
  • 明白了。说得通。我很感激!

标签: ruby-on-rails ruby ssh net-ssh


【解决方案1】:

如果您在没有阻止的情况下启动 SSH 连接,您将获得一个 Net::SSH::Connection::Session,您最终应该在其上调用 close

这里有一个Net::SSH demonstration program,还有一些图片和指向 Net::SSH 整体工作原理的交互式可视化链接,包括close

【讨论】: