问题
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