MAC 地址屬於數據鏈路層,無法直接跨網段獲取。在局域網內,可利用 ping 觸發 ARP 請求,再通過 arp -a 讀取緩存表解析 MAC。
public class MacAddressResolver {
/** 執行單條命令並返回輸出 */
private static String execCmd(String[] cmd) {
StringBuilder output = new StringBuilder();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(Runtime.getRuntime().exec(cmd).getInputStream()))) {
String line;
while ((line = br.readLine()) != null) output.append(line);
} catch (IOException e) {
e.printStackTrace();
}
return output.toString();
}
/** 執行雙命令:先 ping 再 arp */
private static String execSequentialCmd(String[] pingCmd, String[] arpCmd) {
try {
Process pingProc = Runtime.getRuntime().exec(pingCmd);
pingProc.waitFor(); // 等待 ping 完成,確保 ARP 表已更新
return execCmd(arpCmd);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
/** 從命令輸出中提取指定 IP 對應的 MAC */
private static String extractMac(String ip, String output, String separator) {
String regex = "((([0-9A-Fa-f]{1,2}" + separator + "){1,5})[0-9A-Fa-f]{1,2})";
Matcher matcher = Pattern.compile(regex).matcher(output);
while (matcher.find()) {
String mac = matcher.group(1);
// 確保 MAC 位於該 IP 之後(應對多 IP 場景)
if (output.indexOf(ip) <= output.lastIndexOf(mac)) return mac;
}
return "";
}
/** Windows 環境獲取 MAC */
public static String getMacWindows(String ip) {
String[] ping = {"cmd", "/c", "ping " + ip};
String[] arp = {"cmd", "/c", "arp -a"};
return extractMac(ip, execSequentialCmd(ping, arp), "-");
}
/** Linux 環境獲取 MAC */
public static String getMacLinux(String ip) {
String[] cmd = {"/bin/sh", "-c", "ping " + ip + " -c 2 && arp -a"};
return extractMac(ip, execCmd(cmd), ":");
}
/** 統一入口:優先嘗試 Windows,失敗再試 Linux */
public static String getMacAddress(String ip) {
String mac = getMacWindows(ip).trim();
return (mac.isEmpty()) ? getMacLinux(ip).trim() : mac;
}
}
cap_net_raw 能力ping 設置超時,避免不可達 IP 導致阻塞NetworkInterface.getByInetAddress() 直接查詢本機網卡,無需執行命令內網設備管理、軟件授權綁定、訪問控制白名單等純局域網場景。公網環境請勿依賴此方案。