安卓获取所在局域网的其他设备IP

记一次项目中的需求,获取安卓设备所在局域网的其他设备的IP和MAC地址
通过查询资料,发现几种方式

  • shell 执行 ping 的形式,通了则存在设备
    • 耗时
  • arp -a 查询路由表
    • 安卓不支持此命令,只能通过查询文件代替cat proc/net/arp
    • 不能确保最新
  • 通过发送个udp包,再拿到最新的arp
    • 发通了则存在设备(不用管接收的设备是否处理udp包)
    • 本文采用此方式实现
    • 只需1-2秒时间,就能获取局域网设备IP和MAC地址

IP搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.*
import kotlin.Exception
import kotlin.concurrent.thread

object IpScan {

// 获取本机ip
fun hostIp(): String {
var hostIp: String = ""
try {
val nis = NetworkInterface.getNetworkInterfaces()
for (ni in nis) {
val ias = ni.inetAddresses
for (ia in ias) {
// 跳过 ipv6
if (ia is Inet6Address) {
continue
}
if (ia.hostAddress != "127.0.0.1") {
hostIp = ia.hostAddress
break
}
}
}
} catch (e: Exception) {
Log.e("IP扫描", "获取本机IP报错:${e.message.toString()}")
}
return hostIp
}

/**
* @Description 开始扫描ip 耗时大约 1s
* @author lecoler
* @param start:Int 开始的ip (范围:1-254)
* @param end:Int 结束的ip (范围:1-254)
* @param CB: (HashMap<String, String>) -> Unit 结果回调
**/
fun start(start: Int, end: Int, CB: (HashMap<String, String>) -> Unit) {
// 本机ip
val hostIp = hostIp()
// 取前缀
val ipPrefix = hostIp.substring(0, hostIp.lastIndexOf(".") + 1)

thread {
try {
val dp = DatagramPacket(ByteArray(0), 0, 0)
var socket: DatagramSocket = DatagramSocket()
var position: Int = start
// 发送udp包,发通则该ip 存在设备
while (position <= end) {
dp.address = InetAddress.getByName("$ipPrefix$position")
socket.send(dp)
position++
// 分两段掉包,一次性发的话,达到236左右,会耗时3秒左右再往下发
if (position == start + (end-start) / 2) {
socket = DatagramSocket()
}
}
socket.close()
// 获取到arp
val list = catArp()
// 过滤
for(i in list){
val ipSuffix = i.value.split('.').last().toInt()
// 不在范围内,移除
if(ipSuffix < start || ipSuffix > end){
list.remove(i.key)
}
}
CB(list)
} catch (e: Exception) {
Log.e("IP扫描", "扫描报错:${e.message.toString()}")
}
}
}

// 读取 arp表
fun arp(): HashMap<String, String> {
val list: HashMap<String, String> = HashMap()
try {
val exec = Runtime.getRuntime().exec("cat proc/net/arp")
val br = BufferedReader(InputStreamReader(exec.inputStream))
do {
val line = br.readLine()
// 过滤
if (line != null && !line.contains("00:00:00:00:00:00") && !line.contains("IP")) {
val str = line.split("\\s+".toRegex())
val ip:String = str[3]
val mac = str[0]
list[ip] = mac
}
} while (line != null)
} catch (e: Exception) {
Log.e("IP扫描", "读取ARP报错:${e.message.toString()}")
}
return list
}
}