Nunca termina de leer la respuesta del servidor usando jSch

Estoy intentando ejecutar comandos en el servidor Unix conectándome a través de la biblioteca jSch0.1.49. He revisado las muestras proporcionadas por jSch e incluso http://sourceforge.net/apps/mediawiki/jsch/index.php?title=Official_examples

Puedo leer la respuesta del servidor e imprimirla en la consola, pero el ciclo * nunca termina * g. Dudo que el Channele no cierre una vez que haya terminado de leer la respuesta del servidor.

while (true) { while (inputStream.available() > 0) { int i = inputStream.read(buffer, 0, 1024); if (i < 0) { break; } System.out.print(new String(buffer, 0, i));//It is printing the response to console } System.out.println("done");// It is printing continuously infinite times if (channel.isClosed()) {//It is never closed System.out.println("exit-status: " + channel.getExitStatus()); break; } try{Thread.sleep(1000);}catch(Exception ee){} } 

El canal no se cierra solo cuando no queda entrada. Intente cerrarlo usted mismo después de haber leído todos los datos.

 while (true) { while (inputStream.available() > 0) { int i = inputStream.read(buffer, 0, 1024); if (i < 0) { break; } System.out.print(new String(buffer, 0, i));//It is printing the response to console } System.out.println("done"); channel.close(); // this closes the jsch channel if (channel.isClosed()) { System.out.println("exit-status: " + channel.getExitStatus()); break; } try{Thread.sleep(1000);}catch(Exception ee){} } 

La única vez que va a utilizar un bucle que no cierra manualmente el canal es cuando tiene una entrada de teclado interactivo del usuario. Luego, cuando el usuario hace una 'salida', cambiará el 'getExitStatus' del canal. Si su ciclo es while (channel.getExitStatus () == -1), el ciclo saldrá cuando el usuario haya salido. Aún necesita desconectar el canal y la sesión usted mismo después de detectar un estado de salida .

No aparece en su página de ejemplo, pero JSCH alberga una demostración de teclado interactivo en su sitio. http://www.jcraft.com/jsch/examples/UserAuthKI.java

Incluso su demo, que solía conectar a un sistema AIX sin cambiar ninguno de sus códigos ... ¡no se cierra cuando sale del shell!

Tuve que agregar el siguiente código para que salga correctamente después de haber escrito 'exit' en mi sesión remota:

  channel.connect(); // My added code begins here while (channel.getExitStatus() == -1){ try{Thread.sleep(1000);}catch(Exception e){System.out.println(e);} } channel.disconnect(); session.disconnect(); // My Added code ends here } catch(Exception e){ System.out.println(e); } } 

Creo que mejor que la votación para el estado de salida del canal sería esperar al final del Channel.thread

 Thread t = channel.getThread(); if(t != null) { synchronized(t){ t.wait(); } System.out.println("Channel thread completed, disconnecting session"); session.disconnect(); } 

Agregué un miembro de getThread () al canal que simplemente devuelve el hilo del canal actual, también modifiqué Channel.disconnect () de modo que el miembro del hilo no se ponga a cero si el hilo todavía está vivo.

 if(thread != null) { if(!thread.isAlive()) thread = null; } 

en lugar de

 thread = null; 

en Channel.disconnect ()

Encontré que la solución dada arriba se colgaría. Mi solución elimina el “while (true)”, optando por un enfoque más directo.

  private void sendCommand(Channel channel, String command) { try { // this.channelExec = (ChannelExec) channel; this.channelExec.setCommand(command); //channel.setInputStream(null); channel.setOutputStream(System.out); this.is = channel.getInputStream(); channel.connect(); byte[] buffer = new byte[1024]; while (channel.getExitStatus() == -1) { while (is.available() > 0) { int i = is.read(buffer, 0, 1024); // System.out.println("i= " + i); if (i < 0) { // System.out.println("breaking"); break; } String string = new String(buffer, 0, i); output = output.concat(string); //System.out.println("String= " + string); } if (channel.isClosed()) { //System.out.println("exit-status: " + channel.getExitStatus()); break; } } is.close(); channel.disconnect(); this.session.disconnect(); System.out.println("Done"); } catch (IOException ex) { System.out.println("ERROR: " + ex); Logger.getLogger(SSH.class.getName()).log(Level.SEVERE, null, ex); } catch (JSchException ex) { System.out.println("ERROR: " + ex); Logger.getLogger(SSH.class.getName()).log(Level.SEVERE, null, ex); } } 

También estaba tratando de ejecutar remotamente múltiples comandos usando jSch y obtener el comando de salida a la consola (salida estándar System.out ). También quería esperar a que todos los comandos se ejecutaran por completo en la máquina remota.

Después de jugar un montón y buscar en Google durante horas, pude llegar a lo siguiente:

 import java.io.InputStream; import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSch; import com.jcraft.jsch.Session; public class SSHUtility { public static void main(String[] args) { //Separate multiple commands by semicolon //Do not separate commands with `exit` command, otherwise //commands following `exit` will not execute String commands = "source ~/Downloads/helloworld.sh;echo Mahesha999"; executeCommand("username", "password", "hostname", 22, commands); System.out.println("done"); } public static void executeCommand(String pUser, String pPassword, String pServer, int pPort, String pCommand) { System.out.println("Executing on ssh"); JSch lJSCH; Session lSession; lJSCH = new JSch(); try { lSession = lJSCH.getSession(pUser, pServer, pPort); lSession.setConfig("StrictHostKeyChecking", "no"); lSession.setPassword(pPassword); System.out.println("Connecting session..."); lSession.connect(); System.out.println("Session connected."); ChannelExec lChannelExec = (ChannelExec)lSession.openChannel("exec"); lChannelExec.setCommand(pCommand); ((ChannelExec)lChannelExec).setErrStream(System.err); InputStream ins=lChannelExec.getInputStream(); System.out.println("Connecting exec channel..."); lChannelExec.connect(); System.out.println("exec channel connected."); byte[] tmp=new byte[1024]; System.out.println("Trying to read remote command output..."); while(true) { while(ins.available()>0) { int i=ins.read(tmp, 0, 1024); if(i<0)break; System.out.print(new String(tmp, 0, i)); } if(lChannelExec.isClosed()) { if(ins.available()>0) continue; System.out.println("exit-status: "+lChannelExec.getExitStatus()); break; } try{Thread.sleep(1000);}catch(Exception ee){} } lChannelExec.disconnect(); lSession.disconnect(); } catch(Exception e) { System.out.println(e); } } } /* Output: Executing on ssh Connecting session... Session connected. Connecting exec channel... exec channel connected. Trying to read remote command output... Hello Mahesha999 exit-status: 0 done */ 

Siempre que su comando esté formateado correctamente, he encontrado que Jsch cerrará el canal una vez que el comando esté completo, siempre y cuando lea la salida de manera oportuna. En el ejemplo de JCraft, channel.isClosed () es lo único que verifican para que el comando se complete. Leen el resultado en el mismo hilo que espera que el canal se cierre. Una forma más elegante de hacerlo es crear un hilo separado para leer el resultado. El siguiente ejemplo:

Código Jsch:

  Channel channel = null; int exitStatus = 0; List lines = new ArrayList(); try { channel = session.openChannel("exec"); ((ChannelExec) channel).setCommand(command); // Read output in separate thread Thread stdoutReader = new InputStreamHandler(channel.getInputStream(), "STDOUT", lines); // Run the command ((ChannelExec)channel).setCommand(command); channel.connect(); // Start thread that reads output stdoutReader.start(); // Poll for closed status boolean channelClosed = channel.isClosed(); exitStatus = channel.getExitStatus(); boolean stdOutReaderAlive = stdoutReader.isAlive(); int loopCounter = 0; while (!channelClosed) { if (loopCounter % 60 == 0) { log.info("SSH command '" + command + "' still in while loop checking for after " + (loopCounter/60) + " mins."); } loopCounter++; Thread.sleep(1000); channelClosed = channel.isClosed(); exitStatus = channel.getExitStatus(); stdOutReaderAlive = stdoutReader.isAlive(); } log.info("SSH command '" + command + "' exited while loop with values: channelClosed=" + channelClosed + ", exitStatus=" + exitStatus + ", stdOutReaderAlive=" + stdOutReaderAlive); // finish reading output stdoutReader.join(); exitStatus = channel.getExitStatus(); log.info("SSH command '" + command + "' final exitStatus=" + exitStatus); for (String line : lines) { log.info("SSH output: " + line); } } catch (Exception e) { throw new RuntimeException("Error occured processing SSH request. See nested exception for details.", e); } finally { // Always try to close the channel and session. try { channel.disconnect(); } catch(Exception e) { this.log.error("Error - disconnecting channel", e); } try { session.disconnect(); } catch(Exception e) { this.log.error("Error - disconnecting session", e); } } 

InputStreamHandler:

 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * A lot of debate on how to stop a thread... going with the method given here: http://www.javaspecialists.eu/archive/Issue056.html */ public class InputStreamHandler extends Thread { protected Log logger = LogFactory.getLog(getClass()); private InputStream is = null; private String type = null; private StringBuilder buffer; private List lines; public InputStreamHandler(InputStream is, String type) { this.is = is; this.type = type; } public InputStreamHandler(InputStream is, String type, StringBuilder buffer) { this(is, type); this.buffer = buffer; } public InputStreamHandler(InputStream is, String type, List lines) { this(is, type); this.lines = lines; } public void run() { try { InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); if (buffer != null) { String line = null; while ((line = br.readLine()) != null) { buffer.append(line + "\n"); } } else if (lines != null) { String line = null; while ((line = br.readLine()) != null) { lines.add(line); } } else { // just consume output while (br.readLine() != null) ; } } catch (InterruptedIOException ioe) { // when exception is thrown the interrupt flag is set to false... this will set it back to true Thread.currentThread().interrupt(); } catch (IOException ioe) { if (!isInterrupted()) { throw new RuntimeException("Caught IQException for " + this.type + " " + this.getClass().getName() + ".", ioe); } } finally { closeInputStream(); } } @Override public void interrupt() { super.interrupt(); closeInputStream(); logger.info(this.type + " " + this.getClass().getName() + " thread was interrupted."); } private void closeInputStream() { try { is.close(); } catch (Exception e) { } } }