如何在这里正确关闭 ssh 连接/会话?

how to properly close ssh connection / session here?

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

下面的服务器代码部分 returns 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;
}

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

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

runScript 方法的明显问题是它不会关闭 ssh Connection & Session (ganymed ssh lib),因为它立即 returns BufferedReader(如果我没记错的话),而底层脚本仍在运行。如果我在 return 语句之前关闭它们,BufferedReader 将不完整。 那么如何在底层脚本完成后立即正确关闭 connection/session 呢?

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

我建议您重构代码,以便

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

不要在不保持连接的情况下将 Reader 传递到外部。

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

如果您需要在完成 Reader 之前完成多个步骤,您可能无法将 ResultHandler 包装在 try { ... } catch 块中。在那种情况下,你需要一个不同的机制来确保它最终会被关闭。

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

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

虽然 reader 仍然接收输出(只要输入流/连接处于活动状态),您可能需要循环。您需要以某种方式弄清楚您的操作何时完成。

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

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();
     }
  }
}