import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:convert/convert.dart'; import 'package:crypto/crypto.dart'; import 'package:encrypt/encrypt.dart'; import 'package:pointycastle/asymmetric/api.dart'; import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/flavors.dart'; import 'package:star_lock/network/start_chart_api.dart'; import 'package:star_lock/talk/startChart/command/message_command.dart'; import 'package:star_lock/talk/startChart/constant/ip_constant.dart'; import 'package:star_lock/talk/startChart/constant/listen_addr_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/relay_info_entity.dart'; import 'package:star_lock/talk/startChart/entity/report_information_data.dart'; import 'package:star_lock/talk/startChart/entity/star_chart_register_node_entity.dart'; import 'package:star_lock/tools/deviceInfo_utils.dart'; import 'package:star_lock/tools/storage.dart'; import 'package:uuid/uuid.dart'; class StartChartManage { // 私有构造函数,防止外部直接new对象 StartChartManage._internal(); // 单例对象 static final StartChartManage _instance = StartChartManage._internal(); // 工厂构造函数,返回单例对象 factory StartChartManage() { return _instance; } // 产品昵称 final String _productName = F.navTitle; RawDatagramSocket? _udpSocket; late String remoteHost = ''; // 远程主机地址(服务器返回) late int remotePort = 0; // 远程主机端口(服务器返回) final int localPort = 62289; // 本地端口 int heartbeatIntervalTime = 1; // 心跳包间隔时间(s) Timer? _heartBeatTimer; // 心跳包定时器 bool _heartBeatTimerRunning = false; // 心跳包定时任务发送状态 String ToPeerId = ''; // 对端ID String FromPeerId = ''; // 我的ID /// 客户端注册 Future clientRegister() async { // 从缓存中获取星图注册节点信息 final StarChartRegisterNodeEntity? starChartRegisterNodeInfo = await Storage.getStarChartRegisterNodeInfo(); if (starChartRegisterNodeInfo == null) { _log(text: '开始注册客户端'); final StarChartRegisterNodeEntity requestStarChartRegisterNode = await _requestStarChartRegisterNode(); _saveStarChartRegisterNodeToStorage(requestStarChartRegisterNode); } else { final entity = await Storage.getStarChartRegisterNodeInfo(); _log(text: '获取到星图注册节点信息:$entity'); } } // 中继查询 Future relayQuery() async { final RelayInfoEntity relayInfoEntity = await StartChartApi.to.relayQueryInfo(); _saveRelayInfoEntityToStorage(relayInfoEntity); if (relayInfoEntity.relay_list?.length != 0) { final data = relayInfoEntity.relay_list?[0]; FromPeerId = data?.peerID ?? ''; final parseUdpUrl = _parseUdpUrl(data?.listenAddr ?? ''); remoteHost = parseUdpUrl['host'] ?? ''; remotePort = parseUdpUrl['port'] ?? ''; _log(text: '中继信息:${data}'); } } void closeUdpSocket() { if (_udpSocket != null) { _udpSocket?.close(); } } // 在中继服务器中上线 Future onlineRelayService() async { await relayQuery(); var addressIListenFrom = InternetAddress.anyIPv4; RawDatagramSocket.bind(addressIListenFrom, localPort) .then((RawDatagramSocket socket) { _udpSocket = socket; /// 广播功能 _udpSocket!.broadcastEnabled = true; /// 设置数据接收回调 _onReceiveData(_udpSocket!); // 发送上线消息 //_sendOnlineMessage(); // 发送回声测试消息 //_sendEchoMessage(); // 上报信息 reportInformation(); }).catchError((error) { _log(text: 'Failed to bind UDP socket: $error'); }); } // 接收返回的数据 void _onReceiveData(RawDatagramSocket socket) { socket.listen((RawSocketEvent event) { if (event == RawSocketEvent.read) { Datagram? dg = socket.receive(); try { _log(text: '收到消息---> 长度:${dg?.data?.length}, 数据:${dg?.data}'); } catch (e) { _log(text: '❌ Udp ----> $e'); } } }); } // 上报信息至发现服务 Future reportInformation() async { _log(text: '上报信息至发现服务'); // 构建参数 ReportInformationData data = await _makeReportInformationData(); await StartChartApi.to.reportInformation( reportInformationData: data, ); } // 发送上线消息 void _sendOnlineMessage() { // 组装上线消息 final message = MessageCommand.goOnlineRelay(); _sendMessage(message: message); } // 发送回声测试消息 void _sendEchoMessage() { final message = MessageCommand.echoMessage( ToPeerId: ToPeerId, FromPeerId: FromPeerId, ); _sendMessage(message: message); } // 发送心跳包消息 void sendHeartbeatMessage() { if (_heartBeatTimerRunning) { _log(text: '心跳已经开始了. 请勿重复发送心跳包消息'); return; } _heartBeatTimer ??= Timer.periodic( Duration( seconds: heartbeatIntervalTime, ), (Timer timer) { final List message = MessageCommand.heartbeatMessage(); _sendMessage(message: message); }, ); _heartBeatTimerRunning = true; } // 停止定时发送心跳包 void stopHeartbeat() { _heartBeatTimer?.cancel(); _heartBeatTimer = null; // 清除定时器引用 _heartBeatTimerRunning = false; _log(text: '发送心跳包结束'); } // 发送消息 void _sendMessage({required List message}) { _log(text: '发送给中继的消息体:${message},序列化之后的数据:【${bytesToHex(message)}】'); _udpSocket!.send(message, InternetAddress(remoteHost), remotePort); } // 请求注册节点 Future _requestStarChartRegisterNode() async { // 获取设备信息 final Map deviceInfo = await _getDeviceInfo(); // 发送注册节点请求 final StarChartRegisterNodeEntity response = await StartChartApi.to.starChartRegisterNode( product: _productName, model: '${deviceInfo['brand']}_${deviceInfo['model']}', name: '${deviceInfo['id']}', unique: deviceInfo['id'] ?? Uuid().v1(), ); return response; } // 保存星图注册节点信息至缓存 Future _saveStarChartRegisterNodeToStorage( StarChartRegisterNodeEntity starChartRegisterNodeEntity) async { if (starChartRegisterNodeEntity != null) { await Storage.saveStarChartRegisterNodeInfo(starChartRegisterNodeEntity); _log(text: '注册成功'); } } // 保存星图中继服务器信息至缓存 Future _saveRelayInfoEntityToStorage( RelayInfoEntity relayInfoEntity) async { if (relayInfoEntity != null) { await Storage.saveRelayInfo(relayInfoEntity); } } // 构造上报信息数据参数 Future _makeReportInformationData() async { // 获取当前时间戳 int currentTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; // 获取公钥 final publicKey = await getPublicKey(); // 获取私钥 final privateKey = await getPrivateKey(); // 生成签名 final sign = await _generateSign( currentTimestamp: currentTimestamp, privateKey: privateKey, ); // 获取本机所有ip地址和中继返回的外网地址 final List listenAddrDataList = await _makeListenAddrDataList(); // 从缓存中获取中继信息 final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo(); final RelayServiceData relayServiceData = RelayServiceData( name: relayInfoEntity?.relay_list?[0].name ?? '', listen_addr: relayInfoEntity?.relay_list?[0].listenAddr ?? '', peers_max: relayInfoEntity?.relay_list?[0].peerMax ?? 0, peers_current: relayInfoEntity?.relay_list?[0].peerCurrent ?? 0, ); ReportInformationData data = ReportInformationData( id: FromPeerId, public_key: publicKey, listen_addr: listenAddrDataList, relay_service: relayServiceData, time: currentTimestamp, sign: sign, ); return data; } // 获取本机所有ip地址和中继返回的外网地址 Future> _makeListenAddrDataList() async { final List listenAddrDataList = []; final List localIp = await _getAllIpAddresses(); // 从缓存中获取中继信息 final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo(); if (relayInfoEntity != null && relayInfoEntity.client_addr != null) { listenAddrDataList.add( ListenAddrData( type: ListenAddrTypeConstant.relay, address: IpConstant.udpUrl + relayInfoEntity.client_addr! + ':' + localPort.toString(), ), ); } localIp.forEach((element) { listenAddrDataList.add( ListenAddrData( type: ListenAddrTypeConstant.local, address: IpConstant.udpUrl + element + ':' + localPort.toString(), ), ); }); return listenAddrDataList ?? []; } /// 获取本机所有ip Future> _getAllIpAddresses() async { final List ipAddresses = []; try { final List interfaces = await NetworkInterface.list( includeLoopback: true, type: InternetAddressType.any, ); for (final interface in interfaces) { for (final address in interface.addresses) { if (address.address.isNotEmpty && !IpConstant.reportExcludeIp.contains(address.address)) { ipAddresses.add(address.address); } } } } catch (e) { _log(text: '❌--->获取本机IP时出现错误: $e'); } return ipAddresses ?? []; } void _log({required String text}) { AppLog.log('$_productName=====${text}'); } /// 获取设备信息 Future> _getDeviceInfo() async { final Map deviceInfo = await DeviceInfoUtils.getDeviceInfo(); return deviceInfo; } /// 解析 UDP URL 并提取 IP 地址和端口号 Map _parseUdpUrl(String url) { // 使用正则表达式匹配 IP 地址和端口号 final regex = RegExp(r'udp://(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)') .firstMatch(url); if (regex != null) { final ip = regex.group(1); final portStr = regex.group(2); final port = int.tryParse(portStr ?? ''); if (ip != null && port != null) { return {'host': ip, 'port': port}; } } throw FormatException('无法解析 URL 格式: $url'); } String bytesToHex(List bytes) { return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(''); } // 生成签名sing Future _generateSign({ required int currentTimestamp, required String privateKey, }) async { String resultSign = ''; try { // 2. 将时间戳编码为小端字节序(Little Endian) Uint8List signData = Uint8List(4); signData.buffer .asByteData() .setUint32(0, currentTimestamp, Endian.little); // 3. 使用 SHA-256 对 signData 进行哈希运算 final sha256Hash = sha256.convert(signData); var parser = RSAKeyParser(); final RSAPrivateKey parsePrivateKey = parser.parse('-----BEGIN RSA PRIVATE KEY-----\n' + privateKey) as RSAPrivateKey; } catch (e) { _log(text: '❌--->生成签名时出现错误: $e'); } return resultSign ?? ''; } Future getPublicKey() async { // 从缓存中获取星图注册节点信息 final StarChartRegisterNodeEntity? starChartRegisterNodeInfo = await Storage.getStarChartRegisterNodeInfo(); return starChartRegisterNodeInfo?.peer?.publicKey ?? ''; } Future getPrivateKey() async { // 从缓存中获取星图注册节点信息 final StarChartRegisterNodeEntity? starChartRegisterNodeInfo = await Storage.getStarChartRegisterNodeInfo(); return starChartRegisterNodeInfo?.peer?.privateKey ?? ''; } }