业务场景为:记录用户使用系统情况,哪个帐号在什么时间通过哪个ip地址,哪个mac地址访问的系统。
双网卡的一定要看到最后
完整代码在后面。
一、启动 Windows 命令解释器的一个新实例执行命令
1.单命令
参数是命令行数组,例如:String [] cmd={ "cmd","/c","ping "+ip}
运行 cmd
/C 执行字符串指定的命令然后终止
执行 ping ip
Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。
Runtime.getRuntime().exec(cmd);创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。当没有 Process 对象的更多引用时,不是删掉子进程,而是继续异步执行子进程。
将命令执行输出的内容返回
/**
* 执行cmd命令,返回执行结果
* @param cmd 执行的命令
* @return result 执行结果
* @throws Exception
*/
private static String callCmd(String [] cmd ) throws Exception{
String result="";
String line="";
Process proc=Runtime.getRuntime().exec(cmd);
InputStreamReader is= new InputStreamReader(proc.getInputStream());
BufferedReader br=new BufferedReader(is);
while((line=br.readLine())!=null){
result += line;
}
return result;
}
2.双命令
唯一不同的是多了一个判断
int i=proc.waitFor();//已经执行完第一个命令,准备执行第二个命令
//i代表执行失败的条数
如果i<=0则代表第一个命令执行成功。
i>0则代表第一个命令执行失败
/**
* 执行cmd,another命令,返回another执行结果
* @param cmd 第一个命令
* @param another 第二个命令
* @return 第二个命令返回的执行结果
* @throws Exception
*/
private static String callCmd(String [] cmd ,String [] another) throws Exception{
String result="";
String line="";
Runtime rt=Runtime.getRuntime();
Process proc=rt.exec(cmd);
int i=proc.waitFor();//已经执行完第一个命令,准备执行第二个命令
if(i<=0){//i代表执行失败的条数
//ping失败则不执行
System.out.println("i = "+i);
proc=rt.exec(another);
InputStreamReader is= new InputStreamReader(proc.getInputStream());
BufferedReader br=new BufferedReader(is);
while((line=br.readLine())!=null){
result += line;
}
}
return result;
}
二、根据ip处理命令行执行返回结果,找到匹配的Mac地址
返回结果示例:
C:\WINDOWS\system32>arp -a
接口: 101.51.227.192 --- 0x9
Internet 地址 物理地址 类型
101.51.227.144 90-fb-a6-f9-88-08 动态
101.51.227.254 30-7b-ac-8b-24-52 动态
101.51.227.255 ff-ff-ff-ff-ff-ff 静态
224.0.0.22 01-00-5e-00-00-16 静态
224.0.0.251 01-00-5e-00-00-fb 静态
224.0.0.252 01-00-5e-00-00-fc 静态
239.255.255.250 01-00-5e-7f-ff-fa 静态
255.255.255.255 ff-ff-ff-ff-ff-ff 静态
接口: 192.168.43.143 --- 0xf
Internet 地址 物理地址 类型
192.168.43.1 1e-bf-b4-34-86-80 动态
192.168.43.255 ff-ff-ff-ff-ff-ff 静态
224.0.0.22 01-00-5e-00-00-16 静态
224.0.0.251 01-00-5e-00-00-fb 静态
224.0.0.252 01-00-5e-00-00-fc 静态
239.255.255.250 01-00-5e-7f-ff-fa 静态
255.255.255.255 ff-ff-ff-ff-ff-ff 静态
根据正则表达式过滤出返回内容中的所有mac地址。
将mac地址出现的下标与ip出现的下标进行比较,如果当前mac地址下标第一次大于ip下标则认为是 ip后面对应的mac地址。
就是我们所需要的mac地址。对比时因为网管的原因应排除(ff-ff-ff-ff-ff-ff)防止lastIndexOf对比时出现错误。
/**
* @param ip 目标ip
* @param sourceString 命令处理的结果字符串
* @param macSeparator mac分割符号
* @return mac地址,用上面的分割符号表示
*/
private static String filterMacAddress(final String ip,final String sourceString,final String macSeparator){
String result="";
String regExp="((([0-9,A-F,a-f]{1,2}"+macSeparator+"){1,5})[0-9,A-F,a-f]{1,2})";
// System.out.println("regExp="+regExp);
Pattern pattern=Pattern.compile(regExp);
Matcher matcher=pattern.matcher(sourceString);
while(matcher.find()){
result =matcher.group(1);//此处为符合正则表达式的字符串 也就是mac地址
// System.out.println(sourceString.indexOf(ip));//此处为第一次出现ip匹配的下标
// System.out.println(sourceString.lastIndexOf(matcher.group(1)));//最后一次出现这个mac地址的下标
// System.out.println(matcher.group(1));//此次循环匹配的mac地址
// if(sourceString.indexOf(ip)<=sourceString.lastIndexOf(matcher.group(1))){//单网卡
if(sourceString.indexOf(ip)<=sourceString.lastIndexOf(matcher.group(1))&&!"ff-ff-ff-ff-ff-ff".equals(result)){//双网卡
//如果出现ip的下标小于出现mac地址的下标,则此max地址认为是 ip后面 的mac地址
break;//如果有多个ip,则只匹配本ip对应的Mac地址
}
}
return result;
}
三、根据不同系统,创建执行命令,
现执行ping ip,如果ping通则继续执行,
如果ping不通则不执行。
/**
* Windows系统下 根据ip获取mac地址
* @param ip 目标ip
* @return Mac Address
* @throws Exception
*/
private static String getMacInWindows(final String ip)throws Exception{
String result="";
String [] cmd ={ "cmd","/c","ping "+ip};
String [] another={"cmd","/c","arp -a"};
String cmdResult=callCmd(cmd,another);
result=filterMacAddress(ip, cmdResult, "-");
return result;
}
/**
* Linux系统下 根据ip获取mac地址
* @param ip 目标ip
* @return Mac Address
* @throws Exception
*/
private static String getMacInLinux (final String ip)throws Exception{
String result="";
String [] cmd ={"/bin/sh","-c","ping "+ip+" -c 2 && arp -a"};
String cmdResult=callCmd(cmd);
result=filterMacAddress(ip, cmdResult, ":");
return result;
}
四、写个调用方法,先调用windows获取方法,如果抛出异常,则有可能是linux系统。否则就是其他问题。
public static String getMacAddress(String ip){
String macAddress="";
try {
macAddress=getMacInWindows(ip);
} catch (Exception e) {
try {//如果执行windows命令有异常,有可能是linux系统
macAddress=getMacInLinux(ip);
} catch (Exception e1) {
e1.printStackTrace();
}
}
return macAddress;
}
五、最后写个main方法测试
public static void main(String[] args) {
// String ip="224.0.0.22";
String ip="127.0.0.1";
System.out.println(getMacAddress(ip));
}
完整代码如下:
package test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.tools.ant.types.CommandlineJava.SysProperties;
/**
* 根据ip获取mac地址
* @author songfelicity
* 2020年9月14日 14:28:41
*/
public class GetMacAddress {
/**
* 执行cmd命令,返回执行结果
* @param cmd
* @return
* @throws Exception
*/
private static String callCmd(String [] cmd ) throws Exception{
String result="";
String line="";
Process proc=Runtime.getRuntime().exec(cmd);
InputStreamReader is= new InputStreamReader(proc.getInputStream());
BufferedReader br=new BufferedReader(is);
while((line=br.readLine())!=null){
result += line;
}
return result;
}
/**
* 执行cmd,another命令,返回another执行结果
* @param cmd 第一个命令
* @param another 第二个命令
* @return 第二个命令返回的执行结果
* @throws Exception
*/
private static String callCmd(String [] cmd ,String [] another) throws Exception{
String result="";
String line="";
Runtime rt=Runtime.getRuntime();
Process proc=rt.exec(cmd);
int i=proc.waitFor();//已经执行完第一个命令,准备执行第二个命令
if(i<=0){//i代表执行失败的条数
//ping失败则不执行
System.out.println("i = "+i);
proc=rt.exec(another);
InputStreamReader is= new InputStreamReader(proc.getInputStream());
BufferedReader br=new BufferedReader(is);
while((line=br.readLine())!=null){
result += line;
}
}
return result;
}
/**
* @param ip 目标ip
* @param sourceString 命令处理的结果字符串
* @param macSeparator mac分割符号
* @return mac地址,用上面的分割符号表示
*/
private static String filterMacAddress(final String ip,final String sourceString,final String macSeparator){
String result="";
String regExp="((([0-9,A-F,a-f]{1,2}"+macSeparator+"){1,5})[0-9,A-F,a-f]{1,2})";
// System.out.println("regExp="+regExp);
Pattern pattern=Pattern.compile(regExp);
Matcher matcher=pattern.matcher(sourceString);
while(matcher.find()){
result =matcher.group(1);//此处为符合正则表达式的字符串 也就是mac地址
// System.out.println(sourceString.indexOf(ip));//此处为第一次出现ip匹配的下标
// System.out.println(sourceString.lastIndexOf(matcher.group(1)));//最后一次出现这个mac地址的下标
// System.out.println(matcher.group(1));//此次循环匹配的mac地址
// if(sourceString.indexOf(ip)<=sourceString.lastIndexOf(matcher.group(1))){//单网卡
if(sourceString.indexOf(ip)<=sourceString.lastIndexOf(matcher.group(1))&&!"ff-ff-ff-ff-ff-ff".equals(result)){//双网卡
//如果出现ip的下标小于出现mac地址的下标,则此max地址认为是 ip后面 的mac地址
break;//如果有多个ip,则只匹配本ip对应的Mac地址
}
}
return result;
}
/**
* Windows系统下 根据ip获取mac地址
* @param ip 目标ip
* @return Mac Address
* @throws Exception
*/
private static String getMacInWindows(final String ip)throws Exception{
String result="";
String [] cmd ={ "cmd","/c","ping "+ip};
String [] another={"cmd","/c","arp -a"};
String cmdResult=callCmd(cmd,another);
result=filterMacAddress(ip, cmdResult, "-");
return result;
}
/**
* Linux系统下 根据ip获取mac地址
* @param ip 目标ip
* @return Mac Address
* @throws Exception
*/
private static String getMacInLinux (final String ip)throws Exception{
String result="";
String [] cmd ={"/bin/sh","-c","ping "+ip+" -c 2 && arp -a"};
String cmdResult=callCmd(cmd);
result=filterMacAddress(ip, cmdResult, ":");
return result;
}
public static String getMacAddress(String ip){
String macAddress="";
try {
macAddress=getMacInWindows(ip);
} catch (Exception e) {
try {//如果执行windows命令有异常,有可能是linux系统
macAddress=getMacInLinux(ip);
} catch (Exception e1) {
e1.printStackTrace();
}
}
return macAddress;
}
public static void main(String[] args) {
String ip="224.0.0.22";
System.out.println(getMacAddress(ip));
}
}
测试过程中发现一个bug,因为是通过返回的结果通过indexOf+lastIndexOf进行匹配的。
当我测试的ip写成 1092.168.43.1时
图中涉及两处通过ip匹配都符合,所以此时获取的mac地址是 :ff-ff-ff-ff-ff-ff
当使用ff-ff-ff-ff-ff-ff最后一次下标是 获得的是最后一次出现此mac地址的下标。
此时这个下标是大于ip出现的下标,所以返回的mac地址为ff-ff-ff-ff-ff-ff。
分析:可能是因为我使用双网卡的关系。需要注意一下。