在 Java JSch Shell 中得到两次 shell 提示
Getting shell prompt twice in Java JSch Shell
好的,我遇到了 shell 的响应非常不一致的问题。
例如,欢迎消息可以来自 2 个单独的响应。出于某种原因,当我之后使用任何命令时,提示将在我的响应中出现两次。
我注意到只有在使用我自己的管道 input/output 流时才会出现这些问题。当我使用 System.in
和 System.out
时,这些问题根本不会发生。
这是我使用 System.in
和 System.out
的图片:
使用我自己的管道 input/output 流时:
相关代码下方
SSHConnection.java
package com.displee.ssh;
import com.jcraft.jsch.*;
import java.util.function.Function;
public class SSHConnection {
private static final JSch JSCH_INSTANCE = new JSch();
private Session session;
private ShellSession shellSession;
public SSHConnection(String ip, String username, String password) {
try {
this.session = JSCH_INSTANCE.getSession(username, ip);
this.session.setPassword(password);
this.session.setConfig("StrictHostKeyChecking", "no");
} catch(Exception e) {
e.printStackTrace();
this.session = null;
}
}
public boolean connect() {
try {
session.connect();
return true;
} catch(Exception e) {
e.printStackTrace();
session.disconnect();
return false;
}
}
public boolean isConnected() {
return session.isConnected();
}
public void disconnect() {
session.disconnect();
}
public boolean startShellSession(Function<String, Void> callback) {
try {
shellSession = new ShellSession();
shellSession.start(session, callback);
return true;
} catch(Exception e) {
e.printStackTrace();
return false;
}
}
public void writeShellMessage(String message) {
shellSession.write(message + "\r\n");
}
}
ShellSession.java
package com.displee.ssh;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.Session;
import java.io.*;
import java.util.function.Function;
/**
* A class representing a shell session.
* @author Displee
*/
public class ShellSession {
/**
* If this session is running.
*/
private boolean running = true;
private PipedOutputStream poutWrapper;
private PipedInputStream pin = new PipedInputStream(4096);
private PipedInputStream pinWrapper = new PipedInputStream(4096);
private PipedOutputStream pout;
private String lastCommand;
/**
* Constructs a new {@code ShellSession} {@code Object}.
*/
public ShellSession() {
try {
pout = new PipedOutputStream(pinWrapper);
poutWrapper = new PipedOutputStream(pin);
} catch(Exception e) {
e.printStackTrace();
}
}
/**
* Start this shell session.
* @param session The ssh session.
*/
public void start(Session session, Function<String, Void> callback) {
ChannelShell shellChannel = null;
try {
shellChannel = (ChannelShell) session.openChannel("shell");
shellChannel.setInputStream(pin);
shellChannel.setOutputStream(pout);
shellChannel.connect();
} catch(Exception e) {
e.printStackTrace();
}
if (shellChannel == null) {
return;
}
final Channel channel = shellChannel;
Thread thread = new Thread(() -> {
while (running) {
try {
if (pinWrapper.available() != 0) {
String response = readResponse();
callback.apply(response);
}
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
channel.disconnect();
});
thread.setDaemon(true);
thread.start();
}
/**
* Stop this shell session.
*/
public void stop() {
running = false;
}
/**
* Send a message to the shell.
* @param message The message to send.
*/
public void write(String message) {
lastCommand = message;
try {
poutWrapper.write(message.getBytes());
} catch(IOException e) {
e.printStackTrace();
}
}
/**
* Read the response from {@code pinWrapper}.
* @return The string.
* @throws IOException If it could not read the piped stream.
*/
private synchronized String readResponse() throws IOException {
final StringBuilder s = new StringBuilder();
while(pinWrapper.available() > 0) {
s.append((char) pinWrapper.read());
}
return s.toString();
}
}
我的 JavaFX 控制器:
package com.displee.ui;
import com.displee.ssh.SSHConnection;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
@FXML
private TextArea console;
@FXML
private TextField commandInput;
@Override
public void initialize(URL location, ResourceBundle resources) {
SSHConnection connection = new SSHConnection("host", "username", "password");
connection.connect();
connection.startShellSession(s -> {
console.appendText(s);
return null;
});
commandInput.setOnAction(x -> {
connection.writeShellMessage(commandInput.getText());
commandInput.clear();
});
}
}
完整代码:https://displee.com/upload/SSHProject.zip
我尝试增加输入流的大小,但没有用。
这个问题真让我沮丧。
如果有人知道如何解决我的问题,我将不胜感激。
注意:我还注意到使用管道流时速度很慢。我怎样才能加快速度?
您是否正确处理 CR (carriage return)?
服务器可能在所有情况下都会发送两次提示。但是,如果两个提示之间有 CR,当在 System.out
上打印时,第二个提示将覆盖第一个。如果您的流将 CR 解释为 "new-line" (=line feed),您会收到两个提示。
好的,我遇到了 shell 的响应非常不一致的问题。 例如,欢迎消息可以来自 2 个单独的响应。出于某种原因,当我之后使用任何命令时,提示将在我的响应中出现两次。
我注意到只有在使用我自己的管道 input/output 流时才会出现这些问题。当我使用 System.in
和 System.out
时,这些问题根本不会发生。
这是我使用 System.in
和 System.out
的图片:
使用我自己的管道 input/output 流时:
相关代码下方
SSHConnection.java
package com.displee.ssh;
import com.jcraft.jsch.*;
import java.util.function.Function;
public class SSHConnection {
private static final JSch JSCH_INSTANCE = new JSch();
private Session session;
private ShellSession shellSession;
public SSHConnection(String ip, String username, String password) {
try {
this.session = JSCH_INSTANCE.getSession(username, ip);
this.session.setPassword(password);
this.session.setConfig("StrictHostKeyChecking", "no");
} catch(Exception e) {
e.printStackTrace();
this.session = null;
}
}
public boolean connect() {
try {
session.connect();
return true;
} catch(Exception e) {
e.printStackTrace();
session.disconnect();
return false;
}
}
public boolean isConnected() {
return session.isConnected();
}
public void disconnect() {
session.disconnect();
}
public boolean startShellSession(Function<String, Void> callback) {
try {
shellSession = new ShellSession();
shellSession.start(session, callback);
return true;
} catch(Exception e) {
e.printStackTrace();
return false;
}
}
public void writeShellMessage(String message) {
shellSession.write(message + "\r\n");
}
}
ShellSession.java
package com.displee.ssh;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.Session;
import java.io.*;
import java.util.function.Function;
/**
* A class representing a shell session.
* @author Displee
*/
public class ShellSession {
/**
* If this session is running.
*/
private boolean running = true;
private PipedOutputStream poutWrapper;
private PipedInputStream pin = new PipedInputStream(4096);
private PipedInputStream pinWrapper = new PipedInputStream(4096);
private PipedOutputStream pout;
private String lastCommand;
/**
* Constructs a new {@code ShellSession} {@code Object}.
*/
public ShellSession() {
try {
pout = new PipedOutputStream(pinWrapper);
poutWrapper = new PipedOutputStream(pin);
} catch(Exception e) {
e.printStackTrace();
}
}
/**
* Start this shell session.
* @param session The ssh session.
*/
public void start(Session session, Function<String, Void> callback) {
ChannelShell shellChannel = null;
try {
shellChannel = (ChannelShell) session.openChannel("shell");
shellChannel.setInputStream(pin);
shellChannel.setOutputStream(pout);
shellChannel.connect();
} catch(Exception e) {
e.printStackTrace();
}
if (shellChannel == null) {
return;
}
final Channel channel = shellChannel;
Thread thread = new Thread(() -> {
while (running) {
try {
if (pinWrapper.available() != 0) {
String response = readResponse();
callback.apply(response);
}
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
channel.disconnect();
});
thread.setDaemon(true);
thread.start();
}
/**
* Stop this shell session.
*/
public void stop() {
running = false;
}
/**
* Send a message to the shell.
* @param message The message to send.
*/
public void write(String message) {
lastCommand = message;
try {
poutWrapper.write(message.getBytes());
} catch(IOException e) {
e.printStackTrace();
}
}
/**
* Read the response from {@code pinWrapper}.
* @return The string.
* @throws IOException If it could not read the piped stream.
*/
private synchronized String readResponse() throws IOException {
final StringBuilder s = new StringBuilder();
while(pinWrapper.available() > 0) {
s.append((char) pinWrapper.read());
}
return s.toString();
}
}
我的 JavaFX 控制器:
package com.displee.ui;
import com.displee.ssh.SSHConnection;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
@FXML
private TextArea console;
@FXML
private TextField commandInput;
@Override
public void initialize(URL location, ResourceBundle resources) {
SSHConnection connection = new SSHConnection("host", "username", "password");
connection.connect();
connection.startShellSession(s -> {
console.appendText(s);
return null;
});
commandInput.setOnAction(x -> {
connection.writeShellMessage(commandInput.getText());
commandInput.clear();
});
}
}
完整代码:https://displee.com/upload/SSHProject.zip
我尝试增加输入流的大小,但没有用。
这个问题真让我沮丧。 如果有人知道如何解决我的问题,我将不胜感激。
注意:我还注意到使用管道流时速度很慢。我怎样才能加快速度?
您是否正确处理 CR (carriage return)?
服务器可能在所有情况下都会发送两次提示。但是,如果两个提示之间有 CR,当在 System.out
上打印时,第二个提示将覆盖第一个。如果您的流将 CR 解释为 "new-line" (=line feed),您会收到两个提示。