2

我正在尝试通过 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){}
}
4

6 回答 6

3

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

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) 则循环将在用户退出时退出。 检测到 Exit Status 后,您仍然需要自行断开通道和会话

它没有在他们的示例页面上列出,但 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);
   }
}
于 2013-05-01T14:02:18.097 回答
3

我认为比轮询通道退出状态更好的是等待 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()

于 2015-01-28T15:32:52.697 回答
2

我还尝试使用 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
*/
于 2016-06-24T13:04:53.923 回答
1

我发现上面给出的解决方案会挂起。我的解决方案删除了​​“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);
    }

}
于 2015-09-16T13:38:35.330 回答
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);
        }
    }

输入流处理程序:

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) {
        }
    }
}
于 2016-08-05T18:10:38.113 回答
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);
    }
于 2020-01-02T18:02:34.330 回答