如何在这里正确关闭 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();
}
}
}
我有一个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();
}
}
}