2013-04-30 23 views
1

我試圖通過jSch0.1.49庫連接在unix服務器上運行命令。我已經通過jSch提供的樣品了,甚至http://sourceforge.net/apps/mediawiki/jsch/index.php?title=Official_examples永不停止使用jSch讀取服務器響應

我能夠從服務器讀取響應,並打印了安慰,但循環是* 從未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){} 
} 

回答

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

感謝您的解釋。這清除了我的懷疑。所以在我的情況下,我想保持會話打開並運行基於UI部分的一些用戶活動的多個命令。如果我面對更多問題,我會盡力做到這一點並回復您。 – 2013-05-03 10:22:13

+0

我使用的版本是0.1.53,沒有名爲'close'的公共方法(有這樣一種受保護的方法)。根據我的經驗,當命令成功完成時,Jsch通常會(但並非總是)關閉通道,但我無法在代碼中找到成功發生這種情況的原因。似乎共識是檢查閉合通道或關閉輸出或exitStatus!= 1以瞭解命令是否完整。希望JCraft能解決這個問題。 – splashout 2016-08-04 18:52:28

+1

經過進一步測試後,我確定我的問題與在channel.isClosed()返回true之前不會讀取輸出有關。如果命令的輸出太多,通道永遠不會關閉,因此進程會掛起(在Linux上運行)。所以,至少在一個非交互式的過程中,只要您及時讀取輸出,我就可以確認Jsch將在命令完成時關閉通道。我相信這是一個類似於java.lang.Process的API中提到的問題:「無法及時...讀取輸出流...可能會導致它阻塞。 – splashout 2016-08-04 23:32:18

1

我覺得比輪詢通道出口狀況較好的是等待的Channel.thread

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

我已經添加了getThread()成員到通道,所述端只是返回當前信道線,也我修改Channel.disconnect(),使得螺紋部件是不如果線程還活着,則設置爲零

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

代替

thread = null; 

在Channel.disconnect()

0

我發現,以上給出的解決方案將掛起。我的解決方案刪除了​​「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); 
    } 

} 
0

我還試圖遠程執行使用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 
*/ 
0

前提是你的命令格式正確,我發現,一旦該命令完成Jsch將關閉通道只要您及時閱讀輸出結果即可。在JCraft的例子中,頻道。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) { 
     } 
    } 
} 
相關問題