You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 11:35:13