.NET MAUI获取Android/iOS设备IP地址首次启动有效后续失效问题
问题背景
使用.NET MAUI开发Android端应用,目标框架为net6.0-android,测试设备为三星S10+(运行Android 12),需要实现获取设备IP地址的功能。
现有实现存在稳定复现的异常:应用首次启动时可正常获取IP,后续启动均无法获取正确地址,必须重启手机才能恢复。
原有实现代码如下:
private void GetIPAddressClicked(object sender, EventArgs e) { string interfaceDescription = string.Empty; var result = new List<IPAddress>(); try { var upAndNotLoopbackNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces().Where(n => n.NetworkInterfaceType != NetworkInterfaceType.Loopback && n.OperationalStatus == OperationalStatus.Up); foreach (var networkInterface in upAndNotLoopbackNetworkInterfaces) { var iPInterfaceProperties = networkInterface.GetIPProperties(); var unicastIpAddressInformation = iPInterfaceProperties.UnicastAddresses.FirstOrDefault(u => u.Address.AddressFamily == AddressFamily.InterNetwork); if (unicastIpAddressInformation == null) continue; result.Add(unicastIpAddressInformation.Address); interfaceDescription += networkInterface.Description + "---"; } } catch (Exception ex) { Debug.WriteLine($"Unable to find IP: {ex.Message}"); } finally { string allIpInfo = string.Empty; foreach (var item in result) { allIpInfo += item.ToString() + "---"; } IPAddress.Text = allIpInfo; InterfaceName.Text = interfaceDescription; } }
两种场景的返回结果:
- 正常运行(首次启动):
interfaceDescription="------"allIpInfo="192.168.50.112---127.0.0.1---"
- 异常运行(非首次启动):
interfaceDescription="rmnet_data0"allIpInfo="192.0.0.2"
问题根因
原有实现存在三个核心缺陷,导致高版本Android上获取结果不稳定:
- 接口类型过滤逻辑完全不可靠:Android系统存在大量虚拟网卡、系统预留网卡,
NetworkInterfaceType枚举对这些接口的识别不准,既无法完全过滤回环接口(正常返回里包含127.0.0.1就是证据),也会把移动数据虚拟接口rmnet_data0误判为有效接口。 - IP有效性校验缺失:异常返回的
192.0.0.2属于192.0.0.0/24段,是Android系统用于captive portal(网络连通性检测)的内部预留地址,根本不是设备的真实局域网/公网IP;除此之外回环地址、链路本地地址(169.254段)都属于无效地址,原有逻辑完全没有过滤。 - 取数逻辑依赖接口枚举顺序:原有代码用
FirstOrDefault取第一个匹配的IPv4地址,而Android系统枚举网络接口的顺序不固定,首次启动时WiFi接口排序靠前可以拿到正确IP,后续启动时移动虚拟接口排序靠前就会拿到无效地址。同时NetworkInterface.GetAllNetworkInterfaces()在Android 12及以上版本不会主动关联当前活跃网络,会返回所有系统存在的接口,包含大量无用的预留接口。
修复方案
第一步:补全权限
在Android项目的AndroidManifest.xml中添加必要权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" />
第二步:替换获取逻辑
优先通过Android原生ConnectivityManager获取当前真正处于活跃连通状态的网络,再从对应网络属性中提取IP,同时增加严格的IP有效性校验,兜底逻辑保留网卡枚举但增加接口类型过滤。
修复后的完整代码如下:
private void GetIPAddressClicked(object sender, EventArgs e) { string interfaceDescription = string.Empty; var result = new List<IPAddress>(); try { // IP有效性校验规则:过滤所有系统预留、无实际通信意义的IPv4地址 bool IsValidIp(IPAddress ip) { if (ip.AddressFamily != AddressFamily.InterNetwork) return false; byte[] ipBytes = ip.GetAddressBytes(); // 过滤127.0.0.0/8 回环地址 if (ipBytes[0] == 127) return false; // 过滤169.254.0.0/16 链路本地自动分配地址 if (ipBytes[0] == 169 && ipBytes[1] == 254) return false; // 过滤192.0.0.0/24 系统连通性检测预留地址 if (ipBytes[0] == 192 && ipBytes[1] == 0 && ipBytes[2] == 0) return false; return true; } #if ANDROID // Android平台优先通过系统服务获取当前活跃网络,避免枚举无效虚拟网卡 var connectivityManager = (ConnectivityManager)Platform.CurrentActivity .GetSystemService(Android.Content.Context.ConnectivityService); Network activeNetwork = connectivityManager.ActiveNetwork; if (activeNetwork != null) { LinkProperties linkProps = connectivityManager.GetLinkProperties(activeNetwork); if (linkProps != null) { foreach (LinkAddress addr in linkProps.LinkAddresses) { if (addr.Address is Inet4Address ipv4Addr) { IPAddress ip = IPAddress.Parse(ipv4Addr.HostAddress); if (IsValidIp(ip)) { result.Add(ip); interfaceDescription += linkProps.InterfaceName + "---"; } } } } } #endif // 平台逻辑获取失败时,兜底枚举网卡,增加严格过滤 if (result.Count == 0) { var validInterfaces = NetworkInterface.GetAllNetworkInterfaces() .Where(n => n.OperationalStatus == OperationalStatus.Up) // 仅保留真实物理/拨号接口:WiFi、有线以太网、移动数据PPP接口,过滤虚拟网卡 .Where(n => n.NetworkInterfaceType is NetworkInterfaceType.Wireless80211 or NetworkInterfaceType.Ethernet or NetworkInterfaceType.Ppp); foreach (NetworkInterface iface in validInterfaces) { IPInterfaceProperties ipProps = iface.GetIPProperties(); foreach (UnicastIPAddressInformation unicastAddr in ipProps.UnicastAddresses) { if (IsValidIp(unicastAddr.Address)) { result.Add(unicastAddr.Address); interfaceDescription += iface.Description + "---"; } } } } } catch (Exception ex) { Debug.WriteLine($"获取IP失败: {ex.Message}"); } finally { string allIpInfo = result.Any() ? string.Join("---", result.Select(ip => ip.ToString())) + "---" : string.Empty; IPAddress.Text = allIpInfo; InterfaceName.Text = interfaceDescription; } }
补充说明
- 上述方法获取的是设备当前网络下的局域网IP,如果需要获取设备的公网出口IP,无法通过本地接口枚举得到,必须通过公网HTTP服务查询获取。
- 不要使用
FirstOrDefault直接取第一个匹配的IP,部分场景下设备可能同时连接多个有效网络(比如同时连WiFi和有线网),需要根据业务场景选择对应IP。
内容的提问来源于stack exchange,提问作者Owen Lee
相关产品推荐
相关产品推荐

