java 使用 jSch 读取服务器响应永无止境
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16298279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Never ending of reading server response using jSch
提问by Raju Penumatsa
I am trying to run commands at unix server by connecting through jSch0.1.49 library. I have gone through the samples provided by jSch and even http://sourceforge.net/apps/mediawiki/jsch/index.php?title=Official_examples
我试图通过 jSch0.1.49 库连接在 unix 服务器上运行命令。我已经浏览了 jSch 提供的示例,甚至http://sourceforge.net/apps/mediawiki/jsch/index.php?title=Official_examples
I am able to read the response from the server and printed it to console but the loop is *never endin*g. I doubt why the Channele is not closing once it is finished reading response from server.
我能够从服务器读取响应并将其打印到控制台,但循环是 * 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){}
}
回答by Damienknight
The channel does not close itself when there is no input left. Try closing it yourself after you have read all data.
当没有剩余输入时,通道不会自行关闭。阅读完所有数据后尝试自行关闭它。
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){}
}
The only time you are going to use a loop that doesnt manually close the channel is when you have interactive keyboard input from the user. Then when the user does an 'exit' that will change the channel's 'getExitStatus'. If your loop is while(channel.getExitStatus() == -1) then the loop will exit when the user has exited. You still need to disconnect the channel and session yourself after you detect an Exit Status.
您将使用不手动关闭通道的循环的唯一时间是当您有来自用户的交互式键盘输入时。然后,当用户执行“退出”时,将更改频道的“getExitStatus”。如果您的循环是 while(channel.getExitStatus() == -1) 那么循环将在用户退出时退出。 在检测到 Exit Status 后,您仍然需要自己断开通道和会话。
It is not listed on their example page, but JSCH hosts an interactive keyboard demo on their site. http://www.jcraft.com/jsch/examples/UserAuthKI.java
它没有列在他们的示例页面上,但 JSCH 在他们的网站上托管了一个交互式键盘演示。 http://www.jcraft.com/jsch/examples/UserAuthKI.java
Even their demo, which I used to connect to an AIX system without changing any of their code... does not close when you exit the shell!
甚至他们的演示,我曾经在不更改任何代码的情况下连接到 AIX 系统......当您退出 shell 时,它也不会关闭!
I had to add the following code to get it to exit properly after I had typed 'exit' in my remote session:
在远程会话中键入“退出”后,我必须添加以下代码才能使其正确退出:
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);
}
}
回答by Kemlath
I think better than polling for channel exit status would be to wait for the end of the Channel.thread
我认为比轮询通道退出状态更好的是等待 Channel.thread 的结束
Thread t = channel.getThread();
if(t != null)
{
synchronized(t){
t.wait();
}
System.out.println("Channel thread completed, disconnecting session");
session.disconnect();
}
I've added a getThread() member to Channel that simply returns the current channel thread, also I've modified Channel.disconnect() such that the thread member is not set to zero if the thread is still alive
我已经向 Channel 添加了一个 getThread() 成员,它只是返回当前的通道线程,我还修改了 Channel.disconnect() 使得如果线程仍然存在,则线程成员不会设置为零
if(thread != null)
{
if(!thread.isAlive())
thread = null;
}
instead of
代替
thread = null;
in Channel.disconnect()
在 Channel.disconnect()
回答by Jamie Snipes
I found that the solution given above would hang. My solution removes the "while(true)", opting for a more straight forward approach.
我发现上面给出的解决方案会挂起。我的解决方案删除了“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);
}
}
回答by Mahesha999
I was also trying to remote execute multiple commands using jSch and obtain the command output to the console (standard output System.out
). Also wanted to wait till all commands are executed completely on the remote machine.
我还尝试使用 jSch 远程执行多个命令并将命令输出获取到控制台(标准输出System.out
)。还想等到所有命令在远程机器上完全执行。
After fiddling a lot and googling for hours, I was able to come up with the following:
在摆弄了很多东西并在谷歌上搜索了几个小时之后,我得出了以下结论:
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
*/
回答by splashout
Provided your command is properly formatted, I have found Jsch will close the channel once the command is complete as long as you read the output in a timely manner. In the JCraft example, channel.isClosed() is the only thing they check for the command being complete. They read the output in the same thread that waits for the channel to close. A fancier way to do this is to create a separate thread to read the output. Example follows:
如果您的命令格式正确,我发现只要您及时阅读输出,Jsch 就会在命令完成后关闭通道。在 JCraft 示例中,channel.isClosed() 是他们检查命令是否完成的唯一内容。它们在等待通道关闭的同一线程中读取输出。一个更好的方法是创建一个单独的线程来读取输出。示例如下:
Jsch Code:
JS代码:
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) {
}
}
}
回答by Mohd Abdul Shoaib
The best solution that I have found to this problem after a lot of trials and errors is to add an "exit" command at the end your command / commands list:
经过大量试验和错误后,我发现此问题的最佳解决方案是在命令/命令列表的末尾添加“退出”命令:
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);
}