Files
MWClash/lib/clash/service.dart
chen08209 676f2d058a Add windows server mode start process verify
Add linux deb dependencies

Add backup recovery strategy select

Support custom text scaling

Optimize the display of different text scale

Optimize windows setup experience

Optimize startTun performance

Optimize android tv experience

Optimize default option

Optimize computed text size

Optimize hyperOS freeform window

Add developer mode

Update core

Optimize more details
2025-05-01 00:02:29 +08:00

161 lines
3.8 KiB
Dart

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<ServerSocket> serverCompleter = Completer();
Completer<Socket> 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<Uint8List, String>.fromHandlers(
handleData: (Uint8List data, EventSink<String> 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<bool> preload() async {
await serverCompleter.future;
return true;
}
}
final clashService = system.isDesktop ? ClashService() : null;