永远不会使用jSch读取服务器响应

时间:2013-04-30 11:02:13

标签: java jsch

我试图通过连接jSch0.1.49库在unix服务器上运行命令。我已经浏览了jSch甚至http://sourceforge.net/apps/mediawiki/jsch/index.php?title=Official_examples

提供的示例

我能够从服务器读取响应并将其打印到控制台,但循环是* 永远不会结束 * g。我怀疑为什么Channele在完成从服务器读取响应后没有关闭。

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

6 个答案:

答案 0 :(得分:2)

当没有输入时,通道不会自行关闭。阅读完所有数据后,请尝试自行关闭它。

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

当您使用来自用户的交互式键盘输入时,您唯一一次使用不会手动关闭通道的循环。然后,当用户执行“退出”时,将更改频道的'getExitStatus'。如果你的循环是while(channel.getExitStatus()== -1),那么当用户退出时循环将退出。 检测到退出状态后,您仍需要自行断开频道和会话。

它没有在他们的示例页面上列出,但JSCH在其站点上托管了一个交互式键盘演示。 http://www.jcraft.com/jsch/examples/UserAuthKI.java

甚至他们的演示,我曾经连接到AIX系统而不改变他们的任何代码......当你退出shell时它不会关闭!

在我的远程会话中键入“exit”后,我必须添加以下代码才能使其正常退出:

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

答案 1 :(得分:2)

我认为比轮询退出状态更好的方法是等待Channel.thread结束

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

我已经向Channel添加了一个getThread()成员,它只返回当前的通道线程,我也修改了Channel.disconnect(),这样如果线程成员,则线程成员不会设置为零还活着

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

而不是

thread = null;
在Channel.disconnect()

答案 2 :(得分:1)

我发现上面给出的解决方案会挂起。我的解决方案删除了​​&#34; while(true)&#34;,选择了更直接的方法。

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

}

答案 3 :(得分:1)

我还尝试使用jSch远程执行多个命令并获取命令输出到控制台(标准输出System.out)。还想等到所有命令在远程机器上完全执行。

经过多次摆弄和谷歌搜索几个小时后,我能够提出以下建议:

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

答案 4 :(得分:1)

如果您的命令格式正确,我发现只要您及时读取输出,Jsch将在命令完成后关闭通道。在JCraft示例中,channel.isClosed()是他们检查命令完成的唯一内容。他们在等待通道关闭的同一线程中读取输出。更好的方法是创建一个单独的线程来读取输出。示例如下:

Jsch代码:

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

答案 5 :(得分:0)

经过多次尝试和错误,我发现此问题的最佳解决方案是在命令/命令列表的末尾添加“退出”命令:

String[] commands = { 
                "sudo yum update -y",
                "sudo yum install java-1.8.0-openjdk-devel -y",
                "exit"};
String commandsList = String.join(";", commands);
Channel channel = session.openChannel("shell");
    OutputStream ops = channel.getOutputStream();
    PrintStream ps = new PrintStream(ops, true);

    channel.connect();
    ps.println(commandsList);

    InputStream input = channel.getInputStream();
    BufferedReader reader = null;
    String line;

    reader = new BufferedReader(new InputStreamReader(input));
    while ((line = reader.readLine()) != null) {
        line = reader.readLine();
        System.out.println(line);
    }