Из того, что я наблюдал до сих пор, channel.isClosed()
верно только после того, как демон SSH отправил вам все содержимое выходных потоков (stdout и stderr).
Если этот контент большой, он не будет полностью помещаться в буфер TCP-соединения между sshd и вашим процессом, и канал останется открытым навсегда.Проблема еще хуже, когда у вас может быть много контента как в stdout, так и в stderr, потому что вы не знаете, какой поток использовать в приоритете.Звонки на read()
блокируются без возможности тайм-аута.
Решение, которое мы используем, заключается в том, чтобы осторожно потреблять все содержимое stdout и stderr одновременно в главном потоке, а не вызывать read()
в любом потоке, если мы не уверены, что он не заблокируется.
StringBuilder result = new StringBuilder();
StringBuilder error = new StringBuilder();
InputStream out = channel.getInputStream();
InputStream err = channel.getErrStream();
while(!channel.isClosed() || out.available() > 0 || err.available() > 0) {
if (out.available() > 0) {
size = out.read(cbuf);
if (size > 0) {
result.append(new String(cbuf, 0, size));
}
// normally the 'if' is useless as available() guarantee size > 0
// it's just an extra-precaution
} else if (err.available() > 0) {
size = err.read(cbuf);
if (size > 0) {
error.append(new String(cbuf, 0, size));
}
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}