【问题标题】:Never ending of reading server response using jSch使用 jSch 读取服务器响应永无止境
【发布时间】:2013-04-30 11:02:13
【问题描述】:

我正在尝试通过 jSch0.1.49 库连接在 unix 服务器上运行命令。我浏览过 jSch 甚至http://sourceforge.net/apps/mediawiki/jsch/index.php?title=Official_examples提供的示例

我能够从服务器读取响应并将其打印到控制台,但循环是 *never endin*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){}
}

【问题讨论】:

    标签: java jsch


    【解决方案1】:

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

    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 时也不会关闭!

    在远程会话中输入“退出”后,我必须添加以下代码才能正确退出:

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

    【讨论】:

    • 感谢您的解释。它清除了我的疑问。所以就我而言,我想保持会话打开并根据 UI 部分的一些用户活动运行多个命令。如果我遇到更多问题,我会尝试这样做并回复您。
    • 我使用的是 0.1.53 版本,并且没有名为“close”的公共方法(有这样一个受保护的方法)。根据我的经验,当命令成功完成时,Jsch 通常(但不总是)会关闭通道,但我无法在代码中找出成功时发生这种情况的位置。似乎共识是检查关闭的通道或关闭的输出或 exitStatus != 1 以了解命令是否完成。希望 JCraft 能解决这个问题。
    • 经过进一步测试,我确定我的问题与直到 channel.isClosed() 返回 true 后才被读取的输出有关。如果命令的输出过多,则通道将永远不会关闭,因此进程将挂起(顺便说一句,在 Linux 上运行)。所以,至少在非交互的过程中,只要你及时读取输出,我可以确认Jsch会在命令完成时关闭通道。我相信这是一个类似于 java.lang.Process 的 API 中提到的问题:“未能及时...读取输出流...可能导致 [it] 阻塞。
    【解决方案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() 中

    【讨论】:

      【解决方案3】:

      我还尝试使用 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】:

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

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

        【讨论】:

          【解决方案5】:

          如果您的命令格式正确,我发现只要您及时阅读输出,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);
                  }
              }
          

          输入流处理程序:

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

          【讨论】:

            【解决方案6】:

            经过大量试验和错误后,我发现这个问题的最佳解决方案是在你的命令/命令列表末尾添加一个“退出”命令:

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

            【讨论】:

            • Hey Martin..添加了更详细的代码 sn-p 供您参考
            猜你喜欢
            • 2017-01-23
            • 1970-01-01
            • 2012-03-30
            • 2012-12-07
            • 2015-02-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多