使用ganyssh包進行linux連線時讀回顯時readLine發生io阻塞解決方法
原因分析,Ganymed SSH-2這個包感覺不是很好用,比如我根據連線建立一個session的時候,在呼叫exeCommand方法的時候只能用一次,如果想繼續用需要關閉session在一次重新開啟,所以我本地就寫了一個新的命令執行方法,直接獲取輸出流然後以位元組碼的形式flush出去,
public void sendCommand(String cmd) {
try { this.dos.write(cmd.getBytes()); this.dos.write("\r".getBytes()); this.dos.flush(); try { // 每次睡眠一下等待程式相應。 Thread.sleep(400); } catch (InterruptedException ex1) { } } catch (IOException ex) { } }
這樣就可以在一個session中執行多個命令了
在讀取回顯資訊的時候使用的是BufferReader類的readLine方法,但是在讀取最後的回顯時發生了阻塞,程式開在這裡,這個原因是,readLine方法在讀取資料的時候要識別到\n 或者\r,如果返回的回顯中不存在,則會繼續等待資料,導致當前的程式無法進行下去,目前針對這個問題有多種解決的方法,包括讀取位元組碼,然後自己在編碼,或者只讀取字元,使用read方法,但是這些方法都不好,會涉及很多的編碼問題,啊,字串的擷取問題,緩衝區大小,如何動態計算,只能參照java中的readLine去寫,但是都不好,通過查詢資料終於找到了解決辦法就是開啟新的執行緒去執行回顯,這樣就不會影響當前執行緒,我程式中用的是執行緒池,因為執行緒池方便簡單,功能強大,弊端少,最主要我不想寫太多的執行緒程式碼,開啟新的執行緒去執行回顯程式碼後,主程式的依舊會繼續執行,最後只要我們關閉流,使用的執行緒自然會被釋放,所以這是一個很好的方法,建議大家使用,說真的 這個包太尼瑪的坑了。
程式碼如下
package com.ultrapower.Ty.test;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Test;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
public class testJframe3 {
private Session session = null;
private BufferedReader br = null;
private Connection conn = null;
private String hostname = “10.102.45.16”;
private static String username = “tyroot”;
private static String password = “Yhblsqt!#m3”;
public BufferedReader dis = null;
public DataOutputStream dos = null;
// 初始化buffer
private ExecutorService service = Executors.newFixedThreadPool(3);
@Test
public void testSsh() {
try {
auth();
sendCommand("ls");
execCommand();
sendCommand("cd /opt");
sendCommand("ls");
execCommand();
System.out.println("ExitCode: " + session.getExitStatus());
/* Close this session */
closeAll();
} catch (IOException e) {
e.printStackTrace(System.err);
System.exit(2);
}
}
/**
* 讀取回顯
* @throws IOException
*/
public void execCommand() throws IOException {
service.submit(new Runnable() {
// BufferedReader stdout1 = new BufferedReader(new InputStreamReader(new
@Override
public void run() {
String line;
try {
while ((line = dis.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
/**
* 傳送命令。
* @param cmd
*/
public void sendCommand(String cmd) {
try {
this.dos.write(cmd.getBytes());
this.dos.write("\r".getBytes());
this.dos.flush();
try {
// 每次睡眠一下等待程式相應。
Thread.sleep(400);
} catch (InterruptedException ex1) {
}
} catch (IOException ex) {
}
}
/**
*
* @throws IOException
*/
public void auth() throws IOException {
conn = new Connection(hostname, 22);// 首先構造一個聯結器,傳入一個需要登陸的ip地址
/* Now connect */
conn.connect();
System.out.println("connect ok");
/*
* Authenticate. If you get an IOException saying something like
* "Authentication method password not supported by the server at this stage."
* then please check the FAQ.
*/
// 模擬登陸目的伺服器 傳入使用者名稱和密碼 ,
// 它會返回一個布林值,true 代表成功登陸目的伺服器,否則登陸失敗
boolean isAuthenticated = conn.authenticateWithPassword(username, password);
if (isAuthenticated == false)
throw new IOException("Authentication failed.");
this.session = this.conn.openSession();
this.session.requestPTY("vt100", 80, 800, 0, 0, null);
this.session.startShell();
this.dis = new BufferedReader(new InputStreamReader(new StreamGobbler(session.getStdout()), StandardCharsets.UTF_8));
this.dos = new DataOutputStream(this.session.getStdin());
System.out.println("Authentication ok");
/* Create a session */
System.out.println("Here is some information about the remote host:");
}
public void closeAll() throws IOException {
if (this.session != null) {
session.close();
}
if (this.conn != null) {
conn.close();
}
if (this.br != null) {
this.br.close();
}
}
}這裡寫程式碼片