2013-04-30 2 views
1

jSch0.1.49 라이브러리를 통해 연결하여 UNIX 서버에서 명령을 실행하려고합니다. jSch가 제공하는 샘플을 살펴 보았습니다. http://sourceforge.net/apps/mediawiki/jsch/index.php?title=Official_examplesjSch를 사용하여 서버 응답을 읽지 못함

서버에서 응답을 읽을 수 있지만 콘솔에 인쇄 할 수 있지만 루프는 * 결코 끝나지 않습니다 * 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){} 
} 

사용자가 대화 형 키보드 입력을 할 때 채널을 수동으로 닫지 않는 루프를 사용할 때만 가능합니다. 그런 다음 사용자가 'exit'를하면 채널의 'getExitStatus'가 변경됩니다. 루프가 while (channel.getExitStatus() == -1) 인 경우 사용자가 종료하면 루프가 종료됩니다. 종료 상태을 감지 한 후에도 채널과 세션의 연결을 끊어야합니다.

예제 페이지에는 나와 있지 않지만 JSCH는 대화 형 키보드 데모를 호스팅하고 있습니다. http://www.jcraft.com/jsch/examples/UserAuthKI.java

낯선 곳을 벗어나면 코드를 변경하지 않고 AIX 시스템에 연결하는 데 사용했던 데모조차도 닫히지 않습니다!

 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 부분의 일부 사용자 활동을 기반으로 여러 명령을 실행하고 싶습니다. 내가 더 이상 문제에 직면하면 나는 이것을 시도하고 당신에게 돌아올 것입니다. –

+0

버전 0.1.53을 사용하고 있으며 'close'라는 공용 메서드가 없습니다 (보호 된 메서드가 있습니다). 내 경험에 비추어 볼 때 Jsch는 일반적으로 명령이 성공적으로 완료되면 항상 채널을 닫을 것이지만, 성공할 경우 코드에서이를 파악할 수는 없습니다. 닫힌 채널이나 폐쇄 된 출력 또는 exitStatus! = 1을 확인하여 명령이 완료되었는지 확인하는 것이 합당한 것 같습니다. JCraft가이 질문/문제를 해결하길 바랍니다. – splashout

+1

추가 테스트를 마친 후, 문제점이 channel.isClosed()가 true를 반환 할 때까지 읽을 수없는 출력과 관련이 있다고 판단했습니다. 명령의 출력이 너무 많으면 채널이 닫히지 않으므로 프로세스가 중단됩니다 (Linux btw에서 실행). 따라서 최소한 대화식이 아닌 프로세스에서는 출력을 적시에 읽는 한 명령이 완료되면 Jsch가 채널을 닫을 것입니다. 나는 이것이 java.lang.Process 용 API에서 언급 한 것과 비슷한 문제라고 생각한다. "즉각적인 실패로 인해 출력 스트림을 읽으면 차단 될 수 있습니다. – splashout

1

I 채널의 종료 상태에 대한 폴링보다 더 나은 생각을 기다려야하는 것입니다 : 내 원격 세션에서 '종료'를 입력 한 후

나는 제대로 종료 얻기 위해 다음 코드를 추가했다 내가 그 채널에 getThread() 멤버를 추가 한 Channel.thread

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

의 말은 단순히 또한 내가 Channel.disconnect 수정 한, 현재 채널 스레드를 반환() 스레드의 멤버가되지 않도록 쓰레드가 아직 살아 있다면 0으로 설정한다.

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

대신 Channel.disconnect에서

thread = null; 

()

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) { 
     } 
    } 
} 
관련 문제