我有一个客户端/服务器套接字程序。 服务器部分通过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并会使用它,但是我怀疑它能否完全解决问题?)
答案 0 :(得分:1)
我建议您重构代码,以便您
在不保持连接的情况下,请勿将阅读器传递到外部。
下面是一个有关如何完成此操作的简化示例。
如果在使用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();
}
}
}