1. 程式人生 > >Java實現Ping命令

Java實現Ping命令

在專案中需要判斷目錄伺服器是否線上,需要用到ping命令,調研有兩種方法:

  1. 使用Java API的InetAddress方式
  2. 使用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);
}