Java實現Ping命令
阿新 • • 發佈:2019-02-19
在專案中需要判斷目錄伺服器是否線上,需要用到ping命令,調研有兩種方法:
- 使用Java API的InetAddress方式
- 使用Runtime.exec呼叫作業系統的命令CMD
使用InetAddress實現Ping
自Java 1.5開始,java.net包中就實現了ping的功能。詳見InetAddress.isReachable()方法。
public static boolean ping(String ipAddress) throws Exception { int timeOut = 3000 ; //超時應該在3鈔以上 boolean status = InetAddress.getByName(ipAddress).isReachable(timeOut); // 當返回值是true時,說明host是可用的,false則不可。 return status; }
isReachable方法在Windows系統平臺上的實現(native c)並沒有使用ICMP,而是全完使用連線echo埠7 的方法。Native的實現原始碼:
/* * Windows implementation of ICMP & RAW sockets is too unreliable for now. * Therefore it's best not to try it at all and rely only on TCP * We may revisit and enable this code in the future. */ /* Can't create a raw socket, so let's try a TCP socket */ him.sin_port = htons(7); /* Echo */ connect_rv = connect(fd, (struct sockaddr *)&him, len);
InetAddress.isReachable()通過試圖連線TCP埠的方法是利用了TCP/IP協議的三次握手原理,即使對方機器在埠上沒有服務,當接收到請求時會立刻拒絕,如果對方機器不在網路上則結果是超時!這個方法的實現正是利用了這一點。引用OpenJDK 6,isReachable()方法native c實現的一段註釋:
/**
* connection established or refused immediately, either way it means
* we were able to reach the host!
*/
還有一個問題就是超時時間的設定,受網路影響,TCP建立連線的3次握手耗時不確定,例如:
3次握手耗時700ms,如果我們設定的超時時間比700ms小,返回的也是false,從而造成誤報。
呼叫CMD
通過程式呼叫類似“ping 127.0.0.1 -n 10 -w 3000”的命令,該命令ping10次,等待每個響應的超時時間3秒。 網路通的情況會輸出:
C:\Users\tgg>ping 127.0.0.1 -n 10 -w 3000 正在 Ping 127.0.0.1 具有 32 位元組的資料:
來自 127.0.0.1 的回覆: 位元組=32 時間<1ms TTL=64 來自 127.0.0.1 的回覆: 位元組=32 時間<1ms TTL=64
來自 127.0.0.1 的回覆: 位元組=32 時間<1ms TTL=64 來自 127.0.0.1 的回覆: 位元組=32 時間<1ms TTL=64
來自 127.0.0.1 的回覆: 位元組=32 時間<1ms TTL=64 來自 127.0.0.1 的回覆: 位元組=32 時間<1ms TTL=64
來自 127.0.0.1 的回覆: 位元組=32 時間<1ms TTL=64 來自 127.0.0.1 的回覆: 位元組=32 時間<1ms TTL=64
來自 127.0.0.1 的回覆: 位元組=32 時間<1ms TTL=64 來自 127.0.0.1 的回覆: 位元組=32 時間<1ms TTL=64
127.0.0.1 的 Ping 統計資訊: 資料包: 已傳送 = 10,已接收 = 10,丟失 = 0 (0% 丟失), 往返行程的估
計時間(以毫秒為單位): 最短 = 0ms,最長 = 0ms,平均 = 0ms
以上資訊輸出是根據作業系統的語言來進行本地化的,其中"ms TTL="
是不變的,我們可以通過Runtime.exec方法來呼叫本地CMD命令來執行以上語句,程式碼如下:
import org.apache.log4j.Logger;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** * @author tgg */
public class Ping {
public static boolean ping(String ipAddress) throws Exception {
int timeOut = 3000 ;
boolean status = InetAddress.getByName(ipAddress).isReachable(timeOut);
return status;
}
public static boolean ping(String ipAddress, int pingTimes, int timeOut) {
BufferedReader in = null;
Runtime r = Runtime.getRuntime();
// 將要執行的ping命令,此命令是windows格式的命令
String pingCommand = "ping " + ipAddress + " -n " + pingTimes + " -w " + timeOut;
// Linux命令如下
// String pingCommand = "ping" -c " + pingTimes + " -w " + timeOut + ipAddress;
try {
if (logger.isDebugEnabled()) {
logger.debug(pingCommand);
}
// 執行命令並獲取輸出
Process p = r.exec(pingCommand);
if (p == null) {
return false;
}
in = new BufferedReader(new InputStreamReader(p.getInputStream()));
int connectedCount = 0;
String line;
// 逐行檢查輸出,計算類似出現=23ms TTL=62字樣的次數
while ((line = in.readLine()) != null) {
connectedCount += getCheckResult(line);
}
// 如果出現類似=23ms TTL=62這樣的字樣,出現的次數=測試次數則返回真
return connectedCount == pingTimes;
} catch (Exception e) {
logger.error(e);
return false;
} finally {
try {
in.close();
} catch (IOException e) {
logger.error(e);
}
}
}
//若line含有=18ms TTL=16字樣,說明已經ping通,返回1,否則返回0.
private static int getCheckResult(String line) { // System.out.println("控制檯輸出的結果為:"+line);
Pattern pattern = Pattern.compile("(\\d+ms)(\\s+)(TTL=\\d+)", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
return 1;
}
return 0;
}
private static final Logger logger = Logger.getLogger(Ping.class);
}