【问题标题】:how to properly close ssh connection / session here?如何在这里正确关闭 ssh 连接/会话?
【发布时间】:2019-06-21 13:59:49
【问题描述】:

我有一个客户端/服务器套接字程序。 服务器部分通过 ssh 连接到主机,运行脚本,并将输出的每一行发送到客户端。

下面的服务器代码部分返回一个BufferedReader,其中包含脚本输出:

public synchronized BufferedReader runScript(<params>) {    
  BufferedReader br = null ;
  try {
      Connection conn = new Connection(host);
      conn.connect();
      ... // authentication part
      Session sess = conn.openSession();
      sess.execCommand("ascript");
      InputStream stdout = new StreamGobbler(sess.getStdout());
      br = new BufferedReader(new InputStreamReader(stdout));
  } catch (Exception e) {
      e.printStackTrace();
  }
  return br;
}

上面的方法是从另一个服务器端的类/代码中调用的,如下所示,将 BufferedReader 的每一行通过套接字写入客户端,以便客户端在运行时看到脚本的实时输出:

BufferedReader br = new UnixCommandExecutor().runScript(<params>);
String line;
while ((line = br.readLine()) != null) {
      out.writeObject(line);
}

runScript 方法的明显问题是它不会关闭 ssh 连接和会话(ganymed ssh 库),因为它会在底层脚本仍在运行时立即返回 BufferedReader(如果我没记错的话)。如果我在 return 语句之前关闭这些,BufferedReader 将是不完整的。 那么,一旦底层脚本完成,我该如何正确关闭连接/会话呢?

(我知道 try-with-resources 并会使用它,但我怀疑它能否完全解决问题?)

【问题讨论】:

  • 您需要告诉我们您使用的众多 SSH API 中的哪一个,但其中肯定有 close() 方法吗?
  • @user207421 我已经提到了 api(ganymed),当然,其中有一个 close() 方法。这不是问题的重点
  • 这里的实际问题是 我应该把 close() 放在哪里
  • 你把它放在命令完成之后,这将是在你消耗完它的所有输出之后。很明显。
  • 显然会在哪里呢?在runScript() 方法之外?

标签: java ssh client-server


【解决方案1】:

我建议你重构代码,以便你

  • 要么将部件包装到一个对象中,然后再处理关闭操作
  • 或者您立即使用 Reader 并在完成后关闭连接。

在未保持连接的情况下,请勿将 Reader 传递到外部。

下面是一个关于如何完成的稍微简化的示例。

如果您在使用 Reader 之前需要通过多个步骤进行处理,您可能无法将 ResultHandler 包装在 try { ... } catch 块中。在这种情况下,您需要一种不同的机制来确保最终将其关闭。

但从你的问题描述来看,情况可能并非如此。

如果您不想在操作完成之前阻塞(无论如何此操作应该在后台线程中执行),那么您可能希望将收到的每个输出行发送到可以显示的地方。在这种情况下,您应该提供一个用于转发接收到的线路的接口。

当阅读器仍然接收输出时(只要输入流/连接处于活动状态),您可能需要循环。不知何故,您需要弄清楚您的操作何时完成。

例如,您的脚本可以在连接完成后关闭连接(从服务器端),或者返回特定于您的内容,您可以将其解释为操作结束。

public class ResultHandler {

    String host;
    Connection conn;
    BufferedReader reader = null;

    public ResultReader(String host) {
        this.host = host;
    }

    public void connect(<params>) throws Exception {

        // if you intend to reuse the object, just check that it was properly cleanedup before
        close();

        conn = new Connection(host);
        conn.connect();
        ... // authentication part

        // you might want to move the actual handling to a different method
        Session sess = conn.openSession();
        sess.execCommand("ascript");
        InputStream stdout = new StreamGobbler(sess.getStdout());
        br = new BufferedReader(new InputStreamReader(stdout));
    }

    public BufferedReader getReader() {
        return this.reader;
    }

    public void close() {
        If (reader != null) {
            reader.close();
        }
        if (conn != null) {
            conn.close();
        }
    }

    public void finalize() {
        close();
    }
}


synchronized void runScript(<params>) {    

  ResultHandler handler;
  try {
        handler = new ResultHandler(host);
        handler.connect();

        // consume the reader for whatever you need to do

  } catch (Exception e) {
      e.printStackTrace();
  } finally {
    // or use try-with-resource and implement the proper interface for that
    if (handler != null) {
        handler.close();
     }
  }
}

【讨论】:

  • 非常感谢您的建议。我不想立即使用阅读器,因为这会在客户端显示脚本输出时消除“实时效果”。我认为将不同的部分包装到自定义对象中是可行的方法。
猜你喜欢
  • 2023-03-22
  • 2020-09-12
  • 2021-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-12
  • 2017-04-13
  • 2018-01-29
相关资源
最近更新 更多