2024-12-03 21:47:12 +08:00
|
|
|
import 'dart:async';
|
|
|
|
|
import 'dart:convert';
|
2024-04-30 23:38:49 +08:00
|
|
|
import 'dart:io';
|
2024-12-03 21:47:12 +08:00
|
|
|
|
|
|
|
|
import 'package:fl_clash/clash/interface.dart';
|
2024-04-30 23:38:49 +08:00
|
|
|
import 'package:fl_clash/common/common.dart';
|
2024-12-03 21:47:12 +08:00
|
|
|
import 'package:fl_clash/models/core.dart';
|
2025-02-09 18:39:38 +08:00
|
|
|
import 'package:fl_clash/state.dart';
|
2024-12-03 21:47:12 +08:00
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
class ClashService extends ClashHandlerInterface {
|
2024-12-03 21:47:12 +08:00
|
|
|
static ClashService? _instance;
|
|
|
|
|
|
|
|
|
|
Completer<ServerSocket> serverCompleter = Completer();
|
|
|
|
|
|
|
|
|
|
Completer<Socket> socketCompleter = Completer();
|
|
|
|
|
|
2025-02-09 18:39:38 +08:00
|
|
|
bool isStarting = false;
|
|
|
|
|
|
2024-12-03 21:47:12 +08:00
|
|
|
Process? process;
|
|
|
|
|
|
|
|
|
|
factory ClashService() {
|
|
|
|
|
_instance ??= ClashService._internal();
|
|
|
|
|
return _instance!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClashService._internal() {
|
2025-01-13 19:08:17 +08:00
|
|
|
_initServer();
|
|
|
|
|
reStart();
|
2024-12-03 21:47:12 +08:00
|
|
|
}
|
|
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
_initServer() async {
|
2025-02-09 18:39:38 +08:00
|
|
|
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
|
2025-05-02 02:24:12 +08:00
|
|
|
.transform(uint8ListToListIntConverter)
|
|
|
|
|
.transform(utf8.decoder)
|
2025-02-09 18:39:38 +08:00
|
|
|
.transform(LineSplitter())
|
|
|
|
|
.listen(
|
2025-05-02 02:24:12 +08:00
|
|
|
(data) {
|
|
|
|
|
handleResult(
|
|
|
|
|
ActionResult.fromJson(
|
|
|
|
|
json.decode(data.trim()),
|
|
|
|
|
),
|
2025-02-09 18:39:38 +08:00
|
|
|
);
|
2025-05-02 02:24:12 +08:00
|
|
|
},
|
|
|
|
|
);
|
2025-02-09 18:39:38 +08:00
|
|
|
}
|
|
|
|
|
}, (error, stack) {
|
|
|
|
|
commonPrint.log(error.toString());
|
2025-04-18 17:50:46 +08:00
|
|
|
if (error is SocketException) {
|
2025-02-09 18:39:38 +08:00
|
|
|
globalState.showNotifier(error.toString());
|
2025-04-18 17:50:46 +08:00
|
|
|
// globalState.appController.restartCore();
|
2025-02-09 18:39:38 +08:00
|
|
|
}
|
|
|
|
|
});
|
2024-12-03 21:47:12 +08:00
|
|
|
}
|
|
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
@override
|
|
|
|
|
reStart() async {
|
2025-02-09 18:39:38 +08:00
|
|
|
if (isStarting == true) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
isStarting = true;
|
|
|
|
|
socketCompleter = Completer();
|
2024-12-03 21:47:12 +08:00
|
|
|
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()) {
|
2025-04-18 17:50:46 +08:00
|
|
|
final isSuccess = await request.startCoreByHelper(arg);
|
|
|
|
|
if (isSuccess) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-12-03 21:47:12 +08:00
|
|
|
}
|
|
|
|
|
process = await Process.start(
|
|
|
|
|
appPath.corePath,
|
|
|
|
|
[
|
|
|
|
|
arg,
|
|
|
|
|
],
|
|
|
|
|
);
|
2025-05-02 02:24:12 +08:00
|
|
|
process?.stdout.listen((_) {});
|
|
|
|
|
process?.stderr.listen((e) {
|
|
|
|
|
final error = utf8.decode(e);
|
|
|
|
|
if (error.isNotEmpty) {
|
|
|
|
|
commonPrint.log(error);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-02-09 18:39:38 +08:00
|
|
|
isStarting = false;
|
2024-12-03 21:47:12 +08:00
|
|
|
}
|
|
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
@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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-03 21:47:12 +08:00
|
|
|
_deleteSocketFile() async {
|
|
|
|
|
if (!Platform.isWindows) {
|
|
|
|
|
final file = File(unixSocketPath);
|
|
|
|
|
if (await file.exists()) {
|
|
|
|
|
await file.delete();
|
2024-06-03 18:02:05 +08:00
|
|
|
}
|
2024-04-30 23:38:49 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-03 21:47:12 +08:00
|
|
|
_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;
|
2025-01-13 19:08:17 +08:00
|
|
|
return true;
|
2024-12-03 21:47:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
2025-01-13 19:08:17 +08:00
|
|
|
Future<bool> preload() async {
|
|
|
|
|
await serverCompleter.future;
|
|
|
|
|
return true;
|
2024-12-09 01:40:39 +08:00
|
|
|
}
|
2024-04-30 23:38:49 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-03 21:47:12 +08:00
|
|
|
final clashService = system.isDesktop ? ClashService() : null;
|