iOS真机ICMP Ping失败,模拟器及同类APP可正常执行
iOS真机flutter_icmp_ping Ping特定IP超时问题解决
问题背景
使用Flutter开发局域网设备扫描APP,通过flutter_icmp_ping包实现ICMP Ping功能时,发现部分特定IP在iOS真机上返回PingError.requestTimedOut,但模拟器及Fing等同类APP可正常Ping通。相关代码如下:
import 'package:flutter/services.dart'; import 'package:flutter_icmp_ping/flutter_icmp_ping.dart'; import 'package:ip_scanner_ios_new/sidemenu.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; class IcmpPingResult { final int seq; final String response; final double time; final bool success; IcmpPingResult({ required this.seq, required this.response, required this.time, required this.success, }); } class PingPage2 extends StatefulWidget { @override _IcmpPingPageState createState() => _IcmpPingPageState(); } class _IcmpPingPageState extends State<PingPage2> { String _host = 'google.com'; int _count = 5; bool _running = false; List<IcmpPingResult> _results = []; double _packetLoss = 0.0; final _scrollController = ScrollController(); Ping? ping; Future<void> _runPing() async { setState(() { _running = true; _results.clear(); _packetLoss = 0.0; }); try { print('Attempting to ping $_host'); // Debug log ping = Ping( _host, count: _count, timeout: 5, // Increased timeout interval: 1, // Decreased interval ipv6: false, ttl: 64, // Changed TTL ); ping!.stream.listen((event) { print('Raw ping event: $event'); // Debug log if (_results.length < _count) { final time = event.response?.time?.inMilliseconds.toDouble() ?? -1; final success = event.response != null; final response = event.toString(); print('Ping result - Success: $success, Time: $time'); // Debug log setState(() { _results.add(IcmpPingResult( seq: _results.length + 1, response: response, time: time, success: success, )); _packetLoss = ((_count - _results.where((r) => r.success).length) / _count) * 100; }); } }, onDone: () { print('Ping stream completed'); // Debug log setState(() { _running = false; }); }, onError: (e, stack) { print('Ping error: $e'); // Debug log print('Stack trace: $stack'); // Debug log setState(() { _running = false; }); }); } catch (e, stack) { print('Setup error: $e'); // Debug log print('Stack trace: $stack'); // Debug log setState(() { _running = false; }); } } @override void dispose() { ping?.stop(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( drawer: SideMenu(), appBar: AppBar( title: Text('ICMP Ping'), ), body: SafeArea( child: Column( children: [ Padding( padding: EdgeInsets.all(16.0), child: Row( children: [ Expanded( child: TextField( decoration: InputDecoration( hintText: 'Enter host name or IP address', border: OutlineInputBorder(), ), onChanged: (value) { _host = value; }, ), ), SizedBox(width: 16.0), SizedBox( width: 60, child: TextField( keyboardType: TextInputType.number, decoration: InputDecoration( hintText: '#', border: OutlineInputBorder(), ), onChanged: (value) { _count = int.tryParse(value) ?? 5; }, ), ), SizedBox(width: 16.0), ElevatedButton( onPressed: _running ? null : _runPing, child: Text(_running ? 'Running...' : 'Start'), ), ], ), ), Padding( padding: EdgeInsets.symmetric(horizontal: 16.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('${_results.length} of $_count'), Text('${_packetLoss.toStringAsFixed(1)}% packet loss'), ], ), ), Expanded( child: ListView.separated( controller: _scrollController, itemCount: _results.length, itemBuilder: (context, index) { final result = _results[index]; return ListTile( leading: Icon( result.success ? Icons.check_circle : Icons.error, color: result.success ? Colors.green : Colors.red, ), title: Text('Sequence ${result.seq}'), subtitle: Text(result.response), trailing: Text('${result.time.toStringAsFixed(2)} ms'), ); }, separatorBuilder: (context, index) => Divider(), ), ), ], ), ), ); } }
解决方案
1. 完善iOS权限配置
iOS真机对本地网络和ICMP请求有严格权限限制,需在Info.plist中添加以下配置:
<!-- 网络使用说明 --> <key>NSNetworkUsageDescription</key> <string>需要访问网络以扫描局域网设备</string> <!-- 本地网络权限(iOS 13+) --> <key>NSLocalNetworkUsageDescription</key> <string>需要访问本地网络以检测局域网内设备</string> <!-- Bonjour服务支持(可选,针对局域网设备发现) --> <key>NSBonjourServices</key> <array> <string>_http._tcp.</string> <string>_https._tcp.</string> </array>
2. 调整Ping参数与插件选型
- 参数优化:增大超时时间、调整TTL值,尝试开启IPv6支持:
ping = Ping( _host, count: _count, timeout: 10, // 延长至10秒 interval: 1, ipv6: true, // 若局域网支持IPv6可开启 ttl: 128, // 提高TTL值 ); - 更换插件:若
flutter_icmp_ping底层实现存在限制,可尝试ping_discover_network,或直接封装iOS原生SimplePing类实现更稳定的ICMP Ping。
3. 排查网络与设备限制
- 检查iOS设备防火墙:进入「设置-隐私与安全性-防火墙」,临时关闭防火墙测试是否恢复正常。
- 结合ARP扫描:局域网设备检测可搭配ARP扫描(如
arp_scan插件),避免仅依赖ICMP Ping,部分设备可能禁用ICMP但仍可通过ARP检测。
4. 修复代码逻辑缺陷
当前代码通过_results.length < _count判断是否添加结果,可能因事件顺序问题遗漏响应,建议改为基于事件的seq字段判断:
ping!.stream.listen((event) { print('Raw ping event: $event'); final currentSeq = event.response?.seq ?? (_results.length + 1); // 避免重复添加同一seq的结果 if (!_results.any((r) => r.seq == currentSeq)) { final time = event.response?.time?.inMilliseconds.toDouble() ?? -1; final success = event.response != null; final response = event.toString(); setState(() { _results.add(IcmpPingResult( seq: currentSeq, response: response, time: time, success: success, )); _packetLoss = ((_count - _results.where((r) => r.success).length) / _count) * 100; }); } }, onDone: () { // ... });
内容的提问来源于stack exchange,提问作者Panos_42
相关产品推荐
相关产品推荐

