import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:fl_clash/clash/interface.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/models/core.dart'; import 'package:fl_clash/state.dart'; class ClashService extends ClashHandlerInterface { static ClashService? _instance; Completer serverCompleter = Completer(); Completer socketCompleter = Completer(); bool isStarting = false; Process? process; factory ClashService() { _instance ??= ClashService._internal(); return _instance!; } ClashService._internal() { _initServer(); reStart(); } _initServer() async { runZonedGuarded(() async { final address = !Platform.isWindows ? InternetAddress( unixSocketPath, type: InternetAddressType.unix, ) : InternetAddress( localhost, type: InternetAddressType.IPv4, ); await _deleteSocketFile(); final server = await ServerSocket.bind( address, 0, shared: true, ); serverCompleter.complete(server); await for (final socket in server) { await _destroySocket(); socketCompleter.complete(socket); socket .transform( StreamTransformer.fromHandlers( handleData: (Uint8List data, EventSink sink) { sink.add(utf8.decode(data, allowMalformed: true)); }, ), ) .transform(LineSplitter()) .listen( (data) { handleResult( ActionResult.fromJson( json.decode(data.trim()), ), ); }, ); } }, (error, stack) { commonPrint.log(error.toString()); if (error is SocketException) { globalState.showNotifier(error.toString()); // globalState.appController.restartCore(); } }); } @override reStart() async { if (isStarting == true) { return; } isStarting = true; socketCompleter = Completer(); if (process != null) { await shutdown(); } final serverSocket = await serverCompleter.future; final arg = Platform.isWindows ? "${serverSocket.port}" : serverSocket.address.address; if (Platform.isWindows && await system.checkIsAdmin()) { final isSuccess = await request.startCoreByHelper(arg); if (isSuccess) { return; } } process = await Process.start( appPath.corePath, [ arg, ], ); process!.stdout.listen((_) {}); isStarting = false; } @override destroy() async { final server = await serverCompleter.future; await server.close(); await _deleteSocketFile(); return true; } @override sendMessage(String message) async { final socket = await socketCompleter.future; socket.writeln(message); } _deleteSocketFile() async { if (!Platform.isWindows) { final file = File(unixSocketPath); if (await file.exists()) { await file.delete(); } } } _destroySocket() async { if (socketCompleter.isCompleted) { final lastSocket = await socketCompleter.future; await lastSocket.close(); socketCompleter = Completer(); } } @override shutdown() async { if (Platform.isWindows) { await request.stopCoreByHelper(); } await _destroySocket(); process?.kill(); process = null; return true; } @override Future preload() async { await serverCompleter.future; return true; } } final clashService = system.isDesktop ? ClashService() : null;