注:笔者未学习过计算机网络相关的知识,只是在做socket通信项目时接触到这块知识,对一些名词概念使用不当请多多包涵,本篇文章的目的为提供一个笔者原创的Java网络信息获取工具类,并在一定程度上对这个领域的小白指北。
笔者在做一个电脑<-->手机之间进行局域网数据传输的项目,这时就需要调用Java中的socket类,而socket的初始化需要用到对方的ip和端口(当对方为服务端时)。换言之,对于电脑来说,在“电脑开热点手机连接”和“手机开热点电脑连接”两种情况下,我都需要知道手机的ip地址。
在手机端,Android提供了封装好的一个WiFiManager类,通过它能很容易的获取自己和对方的WiFi信息,而通过对“/proc/net/arp”的读取可以得到热点信息,在网上很容易能找到关于它们的代码。
在电脑端,我们可以在Windows的UI界面中看到WiFi的名称和连接热点的设备的名称和ip地址(在“设置->网络和Internet->移动热点”中),但是无法获取WiFi的网关(路由器)的地址。更大的问题是,Java不提供任何像Android中WiFiManager类那样的类,至少我在网上没查到。
Java这条路就算断了,唯一的方向就只有Windows中无敌却又难用的cmd了。笔者通过大量的搜索资料和尝试命令,终于把需要的信息通过cmd获取到了。
为了方便理解,下面的本机(my)指的都是电脑,本机的ip地址称为myIP,与本机进行连接的设备(如手机)称为连接者(connecter),其ip地址称为connecterIP。
一.WiFi部分
1.获取本机ip:在cmd中使用“ipconfig”命令,“无线局域网适配器 WLAN:”下的“IPv4 地址 . . . . . . . . . . . . :”栏即为本机ip。
2.获取连接的WiFi名称:使用“netsh wlan show interfaces”命令,“SSID”栏即为WiFi名称。
3.获取连接者ip:使用“ARP -a”命令,找到“接口:”后的ip地址为本机ip的部分,下面与本机ip属于同一网段且物理地址不为“ff-ff-ff-ff-ff-ff”的即为连接者ip。
二.热点部分
1.获取本机ip:在cmd中使用“ipconfig”命令,“无线局域网适配器 本地连接”下的“IPv4 地址 . . . . . . . . . . . . :”栏即为本机ip。
2.获取连接者ip:使用“ARP -a”命令,找到“接口:”后的ip地址为本机ip的部分,下面与本机ip属于同一网段且物理地址不为“ff-ff-ff-ff-ff-ff”的即为连接者ip。
3.获取连接者的名称:使用“tracert connecterIP”命令(connecterIP为第二步获得的连接者ip),即可跟踪到连接者的名称。(则与Windows UI界面上显示的名称稍有不同(但不影响对设备的识别),但是笔者还没找到获取UI界面上内容的方法)
笔者在Java中获取cmd输出的信息,使用正则表达式提取出其中的信息,并存储在类的属性中,实现了WiFiManager(WiFi管理器)和HotspotManager(热点管理器)两个工具类。
1.Cmd类(获取cmd输出的信息)
(修改自https://blog.csdn.net/w405722907/article/details/78610503,在此对该文章作者表示感谢,之前笔者使用的另一个代码有错,读取内容混乱导致笔者浪费了很多时间)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Cmd {
public static String command(String cmd){//获取cmd输出
try {
Process process = Runtime.getRuntime().exec(cmd);
//关闭流释放资源
if(process != null){
process.getOutputStream().close();
}
InputStream in = process.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
StringBuilder result = new StringBuilder();
String tmp = null;
while ((tmp = br.readLine()) != null) {
result.append(tmp+"\r\n");//将tmp内容附加(append)到StringBuilder类中
}
return result.toString();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
return null;
}
}
}
2.WiFiManager类(需要调用Cmd类的command方法)
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 一个WiFi管理类,用于获取WiFi的信息,用于通信
* @see #isWiFiConnected()
* @see #getMyIP()
* @see #getConnecterIP()
* @see #getConnecterMAC()
* @see #isConnecterHDCP()
*
* @author blademaster
*
*/
public class WiFiManager {
private boolean isWiFiConnected=false;
private String myIP=null;//本机的WiFi ip
private String connecterIP=null;//本机连接的路由的IP
private String connecterMAC=null;//本机连接的路由的MAC
private boolean isConnecterHDCP=false;//本机连接的路由是否为HDCP动态
public WiFiManager() {
// TODO 自动生成的构造函数存根
//******************由"ipconfig"获取myIP*********************
String str=Cmd.command("ipconfig");
str=str+"\r\n";//增加用于确定文字分段的回车
//获取“无线局域网适配器 WLAN”段
String WLAN_str=null;
Pattern pattern = Pattern.compile("无线局域网适配器 WLAN:\r\n\r\n[\\s\\S]*?\r\n\r\n");//设置正则表达形式,[\\s\\S]*?为任意数量字符非贪婪正则表达式
Matcher matcher = pattern.matcher(str);//对str进行正则表达式搜索
if(matcher.find()){
int start = matcher.start();
int end = matcher.end();
WLAN_str=str.substring(start,end);
}
if(WLAN_str==null) {
return;
}
//获取myIP
pattern = Pattern.compile("IPv4 地址 . . . . . . . . . . . . : ");//设置正则表达形式,[\\s\\S]*?为任意数量字符非贪婪正则表达式
matcher = pattern.matcher(WLAN_str);//对str进行正则表达式搜索
if(matcher.find()){
int end = matcher.end();
myIP=WLAN_str.substring(end).split("\r\n")[0];//首先截取匹配到的字符串,然后读到回车,获取ip地址
isWiFiConnected=true;
}
else {
isWiFiConnected=false;
return;
}
//当WiFi连接时
//******************由"ARP -a"获取connecterIP*********************
str=Cmd.command("ARP -a");
str=str+"\r\n";//增加用于确定文字分段的回车
//获取connecterIP
pattern = Pattern.compile("接口: "+myIP+"[\\s\\S]*?类型\r\n");//设置正则表达形式,[\\s\\S]*?为任意数量字符非贪婪正则表达式
matcher = pattern.matcher(str);//对str进行正则表达式搜索
if(matcher.find()){
int end = matcher.end();
String connecter_str=str.substring(end).split("\r\n")[0];//首先截取匹配到的字符串,然后读到回车,获取ip地址
connecterIP=connecter_str.split("\\s+")[1];//"\\s+"为多个空格的正则表达式
connecterMAC=connecter_str.split("\\s+")[2];
isConnecterHDCP=connecter_str.split("\\s+")[3].equals("动态") ? true : false;
}
}
/**
* 判断WiFi是否连接
* @return
*/
public boolean isWiFiConnected() {
return isWiFiConnected;
}
/**
* 返回本机的WLAN ip
* @return
*/
public String getMyIP() {
return myIP;
}
/**
* 返回本机连接的设备的ip
* @return
*/
public String getConnecterIP() {
return connecterIP;
}
/**
* 返回本机连接的设备的MAC
* @return
*/
public String getConnecterMAC() {
return connecterMAC;
}
/**
* 返回本机连接的设备的状态(静态or动态)
* @return
*/
public boolean isConnecterHDCP() {
return isConnecterHDCP;
}
/**
* 获取正在连接的WiFi的名称(SSID)
* @return
*/
public static String getConnectingWiFiSSID() {
String str=Cmd.command("netsh wlan show interfaces");
Pattern pattern = Pattern.compile("SSID[\\s\\S]*?: ");//设置正则表达形式,[\\s\\S]*?为任意数量字符非贪婪正则表达式
Matcher matcher = pattern.matcher(str);//对str进行正则表达式搜索
if(matcher.find()){
int end = matcher.end();
String SSID=str.substring(end).split("\r\n")[0];//首先截取匹配到的字符串,然后读到回车,获取ip地址
return SSID;
}
return null;
}
}
3.HotspotManager类(需要调用Cmd类的command方法)
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 一个热点管理类,用于获取热点的信息,用于通信
* @see #isHotspotConnected()
* @see #getMyIP()
* @see #getConnecterIP()
* @see #getConnecterMAC()
* @see #isConnecterHDCP()
*
* @author blademaster
*
*/
public class HotspotManager {
private boolean isHotspotConnected=false;
private ArrayList localIP=new ArrayList<>();//所有本地连接的地址
private ArrayList myIP=new ArrayList<>();//本机的ip,与connecterIP对应
private ArrayList connecterName=new ArrayList<>();//解析的主机名称
private ArrayList connecterIP=new ArrayList<>();//连接本机的设备的IP
private ArrayList connecterMAC=new ArrayList<>();//连接本机的设备的MAC
private ArrayList isConnecterHDCP=new ArrayList<>();//连接本机的设备是否为HDCP动态,boolean为数据类型,不属于对象,不能作为ArrayList的类型
public HotspotManager() {
// TODO 自动生成的构造函数存根
//******************由"ipconfig"获取myIP*********************
String str=Cmd.command("ipconfig");
str=str+"\r\n";//增加用于确定文字分段的回车
//获取“无线局域网适配器 本地连接”段,由于可能存在多个本地连接,查找所有可能的本地连接
ArrayList Local_str=new ArrayList<>();
Pattern pattern = Pattern.compile("无线局域网适配器 本地连接[\\s\\S]*?\r\n\r\n[\\s\\S]*?\r\n\r\n");//设置正则表达形式,[\\s\\S]*?为任意数量字符非贪婪正则表达式
Matcher matcher = pattern.matcher(str);//对str进行正则表达式搜索
while(matcher.find()){
int start = matcher.start();
int end = matcher.end();
Local_str.add(str.substring(start,end));
}
// 获取myIP
for (int i = 0; i < Local_str.size(); i++) {
pattern = Pattern.compile("IPv4 地址 . . . . . . . . . . . . : ");// 设置正则表达形式,[\\s\\S]*?为任意数量字符非贪婪正则表达式
matcher = pattern.matcher(Local_str.get(i));// 对str进行正则表达式搜索
if (matcher.find()) {
int end = matcher.end();
localIP.add(Local_str.get(i).substring(end).split("\r\n")[0]);// 首先截取匹配到的字符串,然后读到回车,获取ip地址
}
}
if(localIP.isEmpty()) {
isHotspotConnected = false;
return;
}
//当热点连接时
//******************由"ARP -a"获取connecterIP*********************
str=Cmd.command("ARP -a");
str=str+"\r\n";//增加用于确定文字分段的回车
for (int i = 0; i < localIP.size(); i++) {
// 获取connecterIP
pattern = Pattern.compile("接口: " + localIP.get(i) + "[\\s\\S]*?类型\r\n");// 设置正则表达形式,[\\s\\S]*?为任意数量字符非贪婪正则表达式
matcher = pattern.matcher(str);// 对str进行正则表达式搜索
if (matcher.find()) {
int start = matcher.end();
pattern = Pattern.compile("接口: " + localIP.get(i) + "[\\s\\S]*?类型\r\n[\\s\\S]*?\r\n\r\n");// 设置正则表达形式,[\\s\\S]*?为任意数量字符非贪婪正则表达式
matcher = pattern.matcher(str);// 对str进行正则表达式搜索
if (matcher.find()) {//注意要先matcher.find()才能调用matcher.end()
int end = matcher.end()-4;// 除去两个回车(一个"\r\n"两个)
String connecter_str = str.substring(start, end);// 首先截取匹配到的字符串,然后读到回车,获取ip地址
String[] connecter_tag=connecter_str.split("\r\n");
int n=0;
while(!connecter_tag[n].split("\\s+")[2].equals("ff-ff-ff-ff-ff-ff")) {//应该添加局域网的判断,此处省略
myIP.add(localIP.get(i));
connecterIP.add(connecter_tag[n].split("\\s+")[1]);// "\\s+"为多个空格的正则表达式
connecterMAC.add(connecter_tag[n].split("\\s+")[2]);
isConnecterHDCP.add(connecter_tag[n].split("\\s+")[3].equals("动态") ? true : false);
isHotspotConnected = true;//找到至少一个真实的连接者
n++;
}
}
}
}
for(int i=0;i getMyIP() {
return myIP;
}
/**
* 返回连接本机热点的设备的ip(可能有多个)
* @return
*/
public ArrayList getConnecterIP() {
return connecterIP;
}
/**
* 返回连接本机热点的设备的MAC(可能有多个)
* @return
*/
public ArrayList getConnecterMAC() {
return connecterMAC;
}
/**
* 返回连接本机热点的设备的状态(静态or动态)(可能有多个)
* @return
*/
public ArrayList isConnecterHDCP() {
return isConnecterHDCP;
}
/**
* 返回连接本机热点的设备的主机名称(可能有多个)
* @return
*/
public ArrayList getConnecterName() {
return connecterName;
}
/**
* 将热点连接设备的ip通过tracert命令解析为主机名
* @param ip
* @return
*/
public static String IP2Hostname(String ip) {
String str=Cmd.command("tracert "+ip);
if(str.split("\\s+").length < 17) {//当未跟踪到主机名时
return null;
}
String hostname=str.split("\\s+")[15];//通过对cmd输出分析得到
hostname=hostname.split("\\.")[0];//去除.mshome.net后缀
return hostname;
}
}
1.WiFiManager类
通过“WiFiManager wiFiManager=new WiFiManager();”即可创建已获得WiFi信息的对象,然后调用其方法即可获取信息。
由于一般电脑只能连接一个WiFi,所以其对应的信息一般只有一个,返回的信息为String对象
2.HotspotManager类
通过“HotspotManager hotspotManager=new HotspotManager();”即可创建已获得热点信息的对象,然后调用其方法即可获取信息。
由于一台电脑的热点至多可供8台设备连接,所以connecterIP等信息可能不为一个,笔者返回的是ArrayList类的数据,这样便于管理和使用(不懂ArrayList类的同学可以查一下,使用起来非常简单)。