Android(5.0+)Java实现局域网内ESP32 WebServer动态IPv4快速查找方案
解决方案:Android 5.0+ 局域网ESP32 WebServer IP发现
针对Android 12以下版本,提供三个比全段遍历更高效可靠的方案,覆盖路由器、本机热点、他人热点等所有局域网场景:
方案1:自定义UDP广播/组播主动发现(推荐,最快最可靠)
核心逻辑
ESP32端监听指定UDP端口的广播/组播包,收到后立即回复自身IPv4地址和WebServer标识;Android端发送广播/组播请求,接收ESP32的响应即可获取目标IP,无需遍历。
ESP32代码片段(Arduino框架)
#include <WiFi.h> #include <WiFiUdp.h> WiFiUDP udp; const int UDP_PORT = 12345; const String DEVICE_ID = "ESP32_WEBSERVER"; // 自定义设备标识,用于校验响应 void setup() { WiFi.begin("SSID", "PASSWORD"); while (WiFi.status() != WL_CONNECTED) delay(500); udp.begin(UDP_PORT); Serial.println("UDP listener started on port " + String(UDP_PORT)); } void loop() { int packetSize = udp.parsePacket(); if (packetSize) { char incomingPacket[255]; int len = udp.read(incomingPacket, 255); if (len > 0) incomingPacket[len] = 0; // 判断是否是Android端的发现请求 if (strcmp(incomingPacket, "DISCOVER_ESP32") == 0) { String response = DEVICE_ID + ":" + WiFi.localIP().toString(); udp.beginPacket(udp.remoteIP(), udp.remotePort()); udp.print(response); udp.endPacket(); } } delay(10); }
Android代码片段(Java)
import android.os.AsyncTask; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class Esp32DiscoveryTask extends AsyncTask<Void, Void, String> { private static final int UDP_PORT = 12345; private static final String DISCOVER_MSG = "DISCOVER_ESP32"; private static final String DEVICE_ID = "ESP32_WEBSERVER"; private DiscoveryCallback callback; public interface DiscoveryCallback { void onIpFound(String ip); void onFailure(); } public Esp32DiscoveryTask(DiscoveryCallback callback) { this.callback = callback; } @Override protected String doInBackground(Void... voids) { try (DatagramSocket socket = new DatagramSocket()) { socket.setBroadcast(true); socket.setSoTimeout(3000); // 超时3秒 // 发送广播请求 byte[] sendData = DISCOVER_MSG.getBytes(); DatagramPacket sendPacket = new DatagramPacket( sendData, sendData.length, InetAddress.getByName("255.255.255.255"), UDP_PORT ); socket.send(sendPacket); // 接收响应 byte[] receiveData = new byte[255]; DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length); socket.receive(receivePacket); String response = new String(receivePacket.getData()).trim(); if (response.startsWith(DEVICE_ID + ":")) { return response.split(":")[1]; } } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(String ip) { if (ip != null) { callback.onIpFound(ip); } else { callback.onFailure(); } } } // 使用示例 new Esp32DiscoveryTask(new Esp32DiscoveryTask.DiscoveryCallback() { @Override public void onIpFound(String ip) { // 拿到IP后进行HTTP请求 } @Override public void onFailure() { // 处理失败逻辑,比如 fallback 到ARP优化遍历 } }).execute();
方案2:ARP缓存优化遍历(无需修改ESP32)
核心逻辑
读取Android系统的ARP缓存文件/proc/net/arp,获取局域网内已活跃的设备IP,仅对这些IP发送HTTP请求,避免全段255个IP的无效扫描,大幅缩短耗时。
Android代码片段(Java)
import android.os.AsyncTask; import java.io.BufferedReader; import java.io.FileReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; public class ArpScanTask extends AsyncTask<Void, String, String> { private static final String ESP32_WEB_PATH = "/api/device-info"; // ESP32 WebServer的校验路径 private static final int HTTP_TIMEOUT = 500; private ScanCallback callback; public interface ScanCallback { void onIpFound(String ip); void onScanComplete(); } public ArpScanTask(ScanCallback callback) { this.callback = callback; } @Override protected String doInBackground(Void... voids) { List<String> ipList = getLocalNetworkIpsFromArp(); for (String ip : ipList) { if (isEsp32WebServer(ip)) { return ip; } } return null; } // 从ARP缓存读取活跃IP private List<String> getLocalNetworkIpsFromArp() { List<String> ipList = new ArrayList<>(); try (BufferedReader br = new BufferedReader(new FileReader("/proc/net/arp"))) { String line; br.readLine(); // 跳过表头 while ((line = br.readLine()) != null) { String[] parts = line.split("\\s+"); if (parts.length >= 1) { String ip = parts[0]; // 排除本机IP和广播IP if (!ip.equals("127.0.0.1") && !ip.equals("255.255.255.255")) { ipList.add(ip); } } } } catch (Exception e) { e.printStackTrace(); } return ipList; } // 校验是否是目标ESP32 WebServer private boolean isEsp32WebServer(String ip) { try { URL url = new URL("http://" + ip + ESP32_WEB_PATH); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(HTTP_TIMEOUT); conn.setReadTimeout(HTTP_TIMEOUT); conn.setRequestMethod("GET"); int responseCode = conn.getResponseCode(); return responseCode == 200; // 或者校验响应内容 } catch (Exception e) { return false; } } @Override protected void onPostExecute(String ip) { if (ip != null) { callback.onIpFound(ip); } callback.onScanComplete(); } }
方案3:第三方mDNS库实现(兼容ESP32原生mDNS)
核心逻辑
Android 12以下系统不支持系统级mDNS,但可以使用第三方库(如JmDNS)实现mDNS解析,直接发现ESP32的mDNS服务(无需修改ESP32的mDNS配置)。
配置与代码
- 添加Gradle依赖:
dependencies { implementation 'javax.jmdns:jmdns:3.5.5' }
- Android代码片段(Java):
import android.os.AsyncTask; import javax.jmdns.JmDNS; import javax.jmdns.ServiceEvent; import javax.jmdns.ServiceListener; import java.net.InetAddress; public class MdnsDiscoveryTask extends AsyncTask<Void, Void, String> { private static final String SERVICE_TYPE = "_http._tcp.local."; // ESP32 mDNS服务类型 private static final String SERVICE_NAME = "ESP32-WebServer"; // ESP32设置的mDNS服务名 private DiscoveryCallback callback; private JmDNS jmdns; public interface DiscoveryCallback { void onIpFound(String ip); void onFailure(); } public MdnsDiscoveryTask(DiscoveryCallback callback) { this.callback = callback; } @Override protected String doInBackground(Void... voids) { try { InetAddress localAddr = getLocalIpAddress(); jmdns = JmDNS.create(localAddr); jmdns.addServiceListener(SERVICE_TYPE, new ServiceListener() { @Override public void serviceAdded(ServiceEvent event) { jmdns.requestServiceInfo(event.getType(), event.getName(), 1000); } @Override public void serviceRemoved(ServiceEvent event) {} @Override public void serviceResolved(ServiceEvent event) { if (event.getName().equals(SERVICE_NAME)) { String ip = event.getInfo().getInetAddresses()[0].getHostAddress(); publishProgress(ip); jmdns.close(); } } }); Thread.sleep(5000); // 等待5秒获取服务 } catch (Exception e) { e.printStackTrace(); } finally { if (jmdns != null) jmdns.close(); } return null; } @Override protected void onProgressUpdate(String... values) { callback.onIpFound(values[0]); } @Override protected void onPostExecute(String ip) { if (ip == null) { callback.onFailure(); } } // 获取本机局域网IP private InetAddress getLocalIpAddress() throws Exception { // 实现获取本机IPv4地址的逻辑,此处省略,可通过WifiManager获取 return InetAddress.getByName("192.168.43.1"); // 示例IP,需替换为真实逻辑 } }
方案选择建议
- 优先选方案1:主动发现,耗时最短(毫秒级),兼容性最好,仅需ESP32端少量修改。
- 若无法修改ESP32,选方案2:基于ARP缓存的扫描,比全段遍历快5-10倍。
- 若ESP32已配置mDNS,选方案3:无需修改ESP32,利用第三方库实现mDNS解析。
内容的提问来源于stack exchange,提问作者dev.tejasb
相关产品推荐
相关产品推荐

