Never ending of reading server response using jSch

牧云@^-^@ 提交于 2019-12-17 06:49:36

问题


I am trying to run commands at unix server by connecting through jSch0.1.49 library. I have gone through the samples provided by jSch and even http://sourceforge.net/apps/mediawiki/jsch/index.php?title=Official_examples

I am able to read the response from the server and printed it to console but the loop is *never endin*g. I doubt why the Channele is not closing once it is finished reading response from server.

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){}
}

回答1:


The channel does not close itself when there is no input left. Try closing it yourself after you have read all data.

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){}
}

The only time you are going to use a loop that doesnt manually close the channel is when you have interactive keyboard input from the user. Then when the user does an 'exit' that will change the channel's 'getExitStatus'. If your loop is while(channel.getExitStatus() == -1) then the loop will exit when the user has exited. You still need to disconnect the channel and session yourself after you detect an Exit Status.

It is not listed on their example page, but JSCH hosts an interactive keyboard demo on their site. http://www.jcraft.com/jsch/examples/UserAuthKI.java

Even their demo, which I used to connect to an AIX system without changing any of their code... does not close when you exit the shell!

I had to add the following code to get it to exit properly after I had typed 'exit' in my remote session:

     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);
   }
}



回答2:


I think better than polling for channel exit status would be to wait for the end of the Channel.thread

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

I've added a getThread() member to Channel that simply returns the current channel thread, also I've modified Channel.disconnect() such that the thread member is not set to zero if the thread is still alive

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

instead of

thread = null;

in Channel.disconnect()




回答3:


I found that the solution given above would hang. My solution removes the "while(true)", opting for a more straight forward approach.

    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);
    }

}



回答4:


I was also trying to remote execute multiple commands using jSch and obtain the command output to the console (standard output System.out). Also wanted to wait till all commands are executed completely on the remote machine.

After fiddling a lot and googling for hours, I was able to come up with the following:

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
*/



回答5:


Provided your command is properly formatted, I have found Jsch will close the channel once the command is complete as long as you read the output in a timely manner. In the JCraft example, channel.isClosed() is the only thing they check for the command being complete. They read the output in the same thread that waits for the channel to close. A fancier way to do this is to create a separate thread to read the output. Example follows:

Jsch Code:

    Channel channel = null;
    int exitStatus = 0;
    List<String> lines = new ArrayList<String>();
    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<String> 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<String> 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) {
        }
    }
}


来源:https://stackoverflow.com/questions/16298279/never-ending-of-reading-server-response-using-jsch

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!