如何在此处正确关闭ssh连接/会话?

时间:2019-06-21 13:59:49

标签: java ssh client-server

我有一个客户端/服务器套接字程序。 服务器部分通过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并会使用它,但是我怀疑它能否完全解决问题?)

1 个答案:

答案 0 :(得分:1)

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

  • 要么将零件包装到一个对象中,然后再进行关闭操作
  • 或者您立即使用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();
     }
  }
}