diff --git a/README.md b/README.md index 83b61d6..f9fb0f3 100644 --- a/README.md +++ b/README.md @@ -33,13 +33,22 @@ make windows-release ### 🍎 iOS > ios-release -- **证书管理**: 开发环境使用automativally模式自动,生产环境需要下载hiFastVPN-iOs-Prod的profile +- **证书管理**: 开发环境使用automaticall模式自动,生产环境需要下载hiFastVPN-iOs-Prod的profile - ipa发布后使用 `Transporter.app`上传到苹果后台 - release apk:路径在 `dist/对应版本号/*.ipa`, +#### 问题 +- ios真机调试,出现开发环境flutter运行一会就断开,日志没法看,并且生成flutter.log文件,但在xcode可以正常看到日志 + 修改mac上的设置 -> 本机网络 -> android studio 开启 ### 💻 macOS > macos-release - release apk:路径在 `dist/对应版本号/*.dmg`,完成公证和dmg封面制作 +#### 问题 +- 启动过程中遇到 Crash occurred when compiling unknown function in unoptimized JIT mode in unknown pass + 1. xcode修改配置, macOS -> signing Certificate -> 选择 sign to Run Locally; + 2. 使用automaticall +- 启动过程中页面卡在启动页 FFISingboxService - singbox native libs path: "libcore.dylib" + ### 🪟 Windows - 环境需要Inno Setup diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index c7b672b..ea138b8 100755 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -1019,11 +1019,11 @@ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -1251,12 +1251,12 @@ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -1309,11 +1309,11 @@ "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/lib/app/services/singbox_imp/kr_sing_box_imp.dart b/lib/app/services/singbox_imp/kr_sing_box_imp.dart index d2ac83d..633f9be 100755 --- a/lib/app/services/singbox_imp/kr_sing_box_imp.dart +++ b/lib/app/services/singbox_imp/kr_sing_box_imp.dart @@ -22,6 +22,7 @@ import '../../utils/kr_country_util.dart'; import '../../utils/kr_log_util.dart'; import '../../utils/kr_secure_storage.dart'; import '../../utils/kr_windows_dns_util.dart'; +import '../../utils/kr_macos_proxy_util.dart'; import '../../common/app_run_data.dart'; import '../../common/app_config.dart'; import 'package:flutter/foundation.dart'; @@ -297,6 +298,21 @@ class KRSingBoxImp { KRLogUtil.kr_i('⏳ Windows 文件系统同步等待完成', tag: 'SingBox'); } + // 🍎 macOS 平台:初始化时确保代理关闭(防止上次崩溃残留) + if (Platform.isMacOS) { + KRLogUtil.kr_i('🍎 macOS 平台,初始化时清理系统代理残留...', tag: 'SingBox'); + try { + // 异步执行,不阻塞初始化主流程 + KRMacosProxyUtil.disableSocks5().then((_) { + KRLogUtil.kr_i('✅ macOS 系统代理残留清理完成', tag: 'SingBox'); + }).catchError((e) { + KRLogUtil.kr_w('⚠️ macOS 系统代理清理异常: $e', tag: 'SingBox'); + }); + } catch (e) { + KRLogUtil.kr_w('⚠️ macOS 系统代理清理尝试失败: $e', tag: 'SingBox'); + } + } + // 最终验证:在 setup() 之前再次确认 workingDir 和 data 目录都存在且可访问 // libcore 的 Setup() 会调用 os.Chdir(workingPath),然后使用相对路径 "./data" // 如果 os.Chdir() 失败(路径不存在或权限问题),后续的相对路径访问会失败 @@ -1555,6 +1571,19 @@ class KRSingBoxImp { throw err; }).run(); + // 🔑 macOS 平台:启用 SOCKS5 系统代理 + if (Platform.isMacOS) { + KRLogUtil.kr_i('🍎 macOS 平台,开启系统 SOCKS5 代理...', tag: 'SingBox'); + try { + await KRMacosProxyUtil.enableSocks5( + host: '127.0.0.1', + port: kr_port, + ); + } catch (e) { + KRLogUtil.kr_w('⚠️ macOS 系统代理开启失败: $e', tag: 'SingBox'); + } + } + // ⚠️ 关键修复:在启动成功后立即订阅统计流 // 原因: // 1. 统计流需要主动订阅才能接收数据 @@ -1751,6 +1780,16 @@ class KRSingBoxImp { // 不手动设置状态,由 libcore 通过 status stream 自动发送 Stopped 事件 KRLogUtil.kr_i('✅ SingBox 停止请求已发送', tag: 'SingBox'); + + // 🍎 macOS 平台:关闭系统 SOCKS5 代理 + if (Platform.isMacOS) { + KRLogUtil.kr_i('🍎 macOS 平台,关闭系统 SOCKS5 代理...', tag: 'SingBox'); + try { + await KRMacosProxyUtil.disableSocks5(); + } catch (e) { + KRLogUtil.kr_e('❌ macOS 系统代理关闭失败: $e', tag: 'SingBox'); + } + } } catch (e, stackTrace) { KRLogUtil.kr_e('停止服务时出错: $e'); KRLogUtil.kr_e('错误堆栈: $stackTrace'); diff --git a/lib/app/utils/kr_macos_proxy_util.dart b/lib/app/utils/kr_macos_proxy_util.dart new file mode 100644 index 0000000..3f77fb9 --- /dev/null +++ b/lib/app/utils/kr_macos_proxy_util.dart @@ -0,0 +1,113 @@ +import 'dart:io'; +import 'kr_log_util.dart'; + +/// macOS 系统代理管理工具类 +/// +/// 使用 `networksetup` 命令管理 macOS 的 SOCKS5 代理设置 +class KRMacosProxyUtil { + /// 私有构造函数 + KRMacosProxyUtil._(); + + /// 获取所有网络服务列表 + static Future> _listNetworkServices() async { + try { + final r = await Process.run('networksetup', ['-listallnetworkservices']); + if (r.exitCode != 0) { + KRLogUtil.kr_e('列出网络服务失败: ${r.stderr}', tag: 'MacosProxy'); + return []; + } + final lines = (r.stdout as String).split('\n').map((e) => e.trim()).toList(); + // 过滤掉第一行说明文字和空行 + return lines + .where((l) => l.isNotEmpty && !l.toLowerCase().contains('an asterisk')) + .skip(1) + .toList(); + } catch (e) { + KRLogUtil.kr_e('列出网络服务异常: $e', tag: 'MacosProxy'); + return []; + } + } + + /// 为所有网络服务启用 SOCKS5 代理 + static Future enableSocks5({ + required String host, + required int port, + }) async { + if (!Platform.isMacOS) return; + + final services = await _listNetworkServices(); + if (services.isEmpty) { + KRLogUtil.kr_w('未找到可用的网络服务', tag: 'MacosProxy'); + return; + } + + for (final service in services) { + try { + // 1) 设置 SOCKS5 服务器地址和端口 + final r1 = await Process.run('networksetup', [ + '-setsocksfirewallproxy', + service, + host, + port.toString(), + ]); + + if (r1.exitCode != 0) { + KRLogUtil.kr_w('服务 [$service] 设置 SOCKS5 代理失败: ${r1.stderr}', tag: 'MacosProxy'); + continue; + } + + // 2) 开启 SOCKS5 代理状态 + final r2 = await Process.run('networksetup', [ + '-setsocksfirewallproxystate', + service, + 'on', + ]); + + if (r2.exitCode != 0) { + KRLogUtil.kr_w('服务 [$service] 启用 SOCKS5 代理失败: ${r2.stderr}', tag: 'MacosProxy'); + continue; + } + + KRLogUtil.kr_i('✅ 服务 [$service] SOCKS5 代理已启用: $host:$port', tag: 'MacosProxy'); + } catch (e) { + KRLogUtil.kr_e('服务 [$service] 启用代理异常: $e', tag: 'MacosProxy'); + } + } + } + + /// 为所有网络服务禁用 SOCKS5 代理 + static Future disableSocks5() async { + if (!Platform.isMacOS) return; + + final services = await _listNetworkServices(); + if (services.isEmpty) return; + + for (final service in services) { + try { + final r = await Process.run('networksetup', [ + '-setsocksfirewallproxystate', + service, + 'off', + ]); + + if (r.exitCode == 0) { + KRLogUtil.kr_i('✅ 服务 [$service] SOCKS5 代理已关闭', tag: 'MacosProxy'); + } else { + KRLogUtil.kr_w('服务 [$service] 关闭 SOCKS5 代理失败: ${r.stderr}', tag: 'MacosProxy'); + } + } catch (e) { + KRLogUtil.kr_e('服务 [$service] 关闭代理异常: $e', tag: 'MacosProxy'); + } + } + } + + /// 获取指定服务的 SOCKS5 状态 (主要用于调试) + static Future getSocks5Status(String serviceName) async { + try { + final r = await Process.run('networksetup', ['-getsocksfirewallproxy', serviceName]); + return r.stdout as String; + } catch (e) { + return '获取状态失败: $e'; + } + } +} diff --git a/lib/singbox/service/ffi_singbox_service.dart b/lib/singbox/service/ffi_singbox_service.dart index 27b5fcb..7883b0f 100755 --- a/lib/singbox/service/ffi_singbox_service.dart +++ b/lib/singbox/service/ffi_singbox_service.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:ffi'; import 'dart:io'; import 'dart:isolate'; +import 'package:flutter/foundation.dart'; import 'package:kaer_with_panels/utils/isolate_worker.dart'; import 'package:ffi/ffi.dart'; import 'package:fpdart/fpdart.dart'; @@ -31,32 +32,80 @@ class FFISingboxService with InfraLogger implements SingboxService { Stream>? _outboundsStream; static SingboxNativeLibrary _gen() { - String fullPath = ""; - if (Platform.environment.containsKey('FLUTTER_TEST')) { - fullPath = "libcore"; - } - if (Platform.isWindows) { - fullPath = p.join(fullPath, "libcore.dll"); - } else if (Platform.isMacOS) { - fullPath = p.join(fullPath, "libcore.dylib"); - } else { - fullPath = p.join(fullPath, "libcore.so"); - } + String fullPath = _getLibraryPath(); _logger.debug('singbox native libs path: "$fullPath"'); final lib = DynamicLibrary.open(fullPath); return SingboxNativeLibrary(lib); } + static String _getLibraryPath() { + String libName; + if (Platform.isWindows) { + libName = "libcore.dll"; + } else if (Platform.isMacOS) { + libName = "libcore.dylib"; + } else { + libName = "libcore.so"; + } + + // 测试环境 + if (Platform.environment.containsKey('FLUTTER_TEST')) { + return p.join("libcore", libName); + } + + // 🔧 修复:开发环境使用绝对路径 + // 尝试开发环境路径(相对于当前工作目录) + final devPath = p.join("libcore", "bin", libName); + if (kDebugMode) { + print('🔍 [FFI] 检查开发环境路径: $devPath'); + print('🔍 [FFI] 当前工作目录: ${Directory.current.path}'); + print('🔍 [FFI] 文件是否存在: ${File(devPath).existsSync()}'); + } + + if (File(devPath).existsSync()) { + if (kDebugMode) { + print('✅ [FFI] 使用开发环境路径: $devPath'); + } + return devPath; + } + + // 生产环境:使用相对路径(bundle中的路径) + if (kDebugMode) { + print('⚠️ [FFI] 开发环境路径不存在,使用生产环境路径: $libName'); + } + return libName; + } + @override Future init() async { + if (kDebugMode) { + print('🚀 [FFI] init() 开始'); + } loggy.debug("initializing"); - _box.setupOnce(NativeApi.initializeApiDLData); + + // 注意:setupOnce 会在 worker isolate 中调用(见 _ffiLoadLibrary) + // 在主 isolate 中调用会导致阻塞,因此这里跳过 + if (kDebugMode) { + print('⏭️ [FFI] 跳过主 isolate 中的 setupOnce(将在 worker isolate 中执行)'); + } + + if (kDebugMode) { + print('📡 [FFI] 创建 ReceivePort'); + } _statusReceiver = ReceivePort('service status receiver'); + + if (kDebugMode) { + print('🔄 [FFI] 设置状态流'); + } final source = _statusReceiver.asBroadcastStream().map((event) => jsonDecode(event as String)).map(SingboxStatus.fromEvent); _status = ValueConnectableStream.seeded( source, const SingboxStopped(), ).autoConnect(); + + if (kDebugMode) { + print('✅ [FFI] init() 完成'); + } } @override @@ -495,17 +544,7 @@ class FFISingboxService with InfraLogger implements SingboxService { } SingboxNativeLibrary _ffiLoadLibrary() { - String fullPath = ""; - if (Platform.environment.containsKey('FLUTTER_TEST')) { - fullPath = "libcore"; - } - if (Platform.isWindows) { - fullPath = p.join(fullPath, "libcore.dll"); - } else if (Platform.isMacOS) { - fullPath = p.join(fullPath, "libcore.dylib"); - } else { - fullPath = p.join(fullPath, "libcore.so"); - } + final fullPath = FFISingboxService._getLibraryPath(); final lib = DynamicLibrary.open(fullPath); final box = SingboxNativeLibrary(lib); box.setupOnce(NativeApi.initializeApiDLData); diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index 33ec3b3..f3634aa 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ