Android 13下全网络设备IP与MAC扫描的技术求助
网络设备IP与MAC扫描问题及解决方案
问题描述
我正在开发一款扫描网络中所有设备IP与MAC的应用,但目前仅能获取手机类设备的IP,无法扫描到通过网线连接路由器的电脑或笔记本。尝试使用arp -a命令实现,但该方法在Android 13上已失效。观察到Network Scanner和Wi-Fi Monitor等应用可正常获取IP与MAC,附上当前实现代码,寻求正确实现全设备IP与MAC扫描的方案:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private static final int REQUEST_CODE_PERMISSIONS = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_WIFI_STATE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.INTERNET, Manifest.permission.ACCESS_WIFI_STATE }, REQUEST_CODE_PERMISSIONS); } Button scanButton = findViewById(R.id.scanButton); scanButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new ScanNetworkTask().execute(); } }); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CODE_PERMISSIONS) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { findViewById(R.id.scanButton).setEnabled(true); } else { Log.e(TAG, "Permissions denied by user."); findViewById(R.id.scanButton).setEnabled(false); } } } private class ScanNetworkTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... voids) { try { scanNetwork(); } catch (IOException e) { Log.e(TAG, "IOException in scanNetwork: ", e); } return null; } private void scanNetwork() throws IOException { try { NetworkInterface wlan0 = NetworkInterface.getByName("wlan0"); if (wlan0 != null && wlan0.isUp()) { Enumeration<InetAddress> addresses = wlan0.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); if (address.isLoopbackAddress() || !address.isSiteLocalAddress()) { continue; } String subnet = getSubnet(address.getHostAddress()); Log.d(TAG, "Scanning subnet: " + subnet); for (int i = 1; i < 255; i++) { String host = subnet + i; try { InetAddress inetAddress = InetAddress.getByName(host); if (inetAddress.isReachable(1000)) { Log.d(TAG, "Host: " + inetAddress.getHostAddress() + " is reachable"); Log.d(TAG, "Hostname: " + inetAddress.getHostName()); } } catch (IOException e) { Log.e(TAG, "IOException in scanNetwork: ", e); } } } } } catch (SocketException e) { Log.e(TAG, "SocketException in scanNetwork: ", e); } } private String getSubnet(String ipAddress) { return ipAddress.substring(0, ipAddress.lastIndexOf('.') + 1); } } }
解决方案
1. 补充必要权限
Android 13+对网络扫描权限有严格限制,需要添加并动态申请以下权限:
ACCESS_FINE_LOCATION:Wi-Fi扫描必须的位置权限CHANGE_WIFI_STATE:触发Wi-Fi扫描以更新ARP缓存ACCESS_NETWORK_STATE:获取当前网络状态READ_PHONE_STATE:部分设备需要此权限读取网络信息
在AndroidManifest.xml中声明:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
动态申请代码修改:
ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.INTERNET, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CHANGE_WIFI_STATE, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.READ_PHONE_STATE }, REQUEST_CODE_PERMISSIONS);
2. 替代arp -a的ARP缓存读取方案
Android 13禁止普通应用执行arp -a,但可以直接读取系统ARP缓存文件/proc/net/arp,该文件包含所有已发现设备的IP、MAC等信息。
添加读取ARP缓存的方法:
private Map<String, String> readArpCache() throws IOException { Map<String, String> ipMacMap = new HashMap<>(); BufferedReader reader = new BufferedReader(new FileReader("/proc/net/arp")); String line; // 跳过表头行 reader.readLine(); while ((line = reader.readLine()) != null) { String[] parts = line.split("\\s+"); if (parts.length >= 4) { String ip = parts[0]; String mac = parts[3]; // 过滤无效MAC(全0或未分配) if (!mac.equals("00:00:00:00:00:00")) { ipMacMap.put(ip, mac); } } } reader.close(); return ipMacMap; }
3. 优化设备可达性检测
inetAddress.isReachable()依赖ICMP协议,很多有线设备会禁用ping响应,导致无法被检测到。可以结合多端口TCP/UDP探测提高检测率:
private boolean isHostReachable(String ip) { // 尝试扫描常用端口:80(HTTP), 443(HTTPS), 22(SSH), 3389(RDP) int[] ports = {80, 443, 22, 3389}; for (int port : ports) { try (Socket socket = new Socket()) { socket.connect(new InetSocketAddress(ip, port), 500); return true; } catch (IOException ignored) { // 端口不通,尝试下一个 } } // 最后尝试ICMP try { return InetAddress.getByName(ip).isReachable(1000); } catch (IOException e) { return false; } }
4. 动态获取Wi-Fi网络接口
不要硬编码wlan0,不同设备的Wi-Fi接口名称可能不同(如wlan1、eth0),通过遍历网络接口获取正确的Wi-Fi接口:
private NetworkInterface getWifiInterface() throws SocketException { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface ni = interfaces.nextElement(); // 判断是否为Wi-Fi接口:名称包含wlan,且不是回环接口 if (ni.isUp() && !ni.isLoopback() && ni.getName().contains("wlan")) { return ni; } } return null; }
整合后的扫描逻辑
修改scanNetwork方法,结合以上优化点:
private void scanNetwork() throws IOException { NetworkInterface wifiInterface = getWifiInterface(); if (wifiInterface == null) { Log.e(TAG, "Wi-Fi interface not found"); return; } Enumeration<InetAddress> addresses = wifiInterface.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); if (address.isLoopbackAddress() || !address.isSiteLocalAddress()) { continue; } String subnet = getSubnet(address.getHostAddress()); Log.d(TAG, "Scanning subnet: " + subnet); // 并行扫描提高效率(注意线程控制) ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 1; i < 255; i++) { final String host = subnet + i; executor.submit(() -> { if (isHostReachable(host)) { Log.d(TAG, "Host reachable: " + host); } }); } executor.shutdown(); try { executor.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 读取ARP缓存获取MAC地址 Map<String, String> ipMacMap = readArpCache(); for (Map.Entry<String, String> entry : ipMacMap.entrySet()) { Log.d(TAG, "IP: " + entry.getKey() + ", MAC: " + entry.getValue()); } } }
注意事项
- 部分国产ROM可能对
/proc/net/arp的读取有限制,可以尝试使用ConnectivityManager的getAllNetworks()接口辅助获取网络设备信息。 - 并行扫描时要控制线程数量,避免触发系统的网络限流。
- 扫描前可以调用
WifiManager.startScan()触发Wi-Fi扫描,更新ARP缓存,提高设备发现率。
内容的提问来源于stack exchange,提问作者Luis Carlos Bautista Machuca
相关产品推荐
相关产品推荐

