app-starlock/lib/talk/startChart/start_chart_manage.dart

380 lines
12 KiB
Dart
Raw Normal View History

2024-11-28 14:57:49 +08:00
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<void> 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<void> 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<void> 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<void> 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<int> message = MessageCommand.heartbeatMessage();
_sendMessage(message: message);
},
);
_heartBeatTimerRunning = true;
}
// 停止定时发送心跳包
void stopHeartbeat() {
_heartBeatTimer?.cancel();
_heartBeatTimer = null; // 清除定时器引用
_heartBeatTimerRunning = false;
_log(text: '发送心跳包结束');
}
// 发送消息
void _sendMessage({required List<int> message}) {
_log(text: '发送给中继的消息体:${message},序列化之后的数据:【${bytesToHex(message)}');
_udpSocket!.send(message, InternetAddress(remoteHost), remotePort);
}
// 请求注册节点
Future<StarChartRegisterNodeEntity> _requestStarChartRegisterNode() async {
// 获取设备信息
final Map<String, String> 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<void> _saveStarChartRegisterNodeToStorage(
StarChartRegisterNodeEntity starChartRegisterNodeEntity) async {
if (starChartRegisterNodeEntity != null) {
await Storage.saveStarChartRegisterNodeInfo(starChartRegisterNodeEntity);
_log(text: '注册成功');
}
}
// 保存星图中继服务器信息至缓存
Future<void> _saveRelayInfoEntityToStorage(
RelayInfoEntity relayInfoEntity) async {
if (relayInfoEntity != null) {
await Storage.saveRelayInfo(relayInfoEntity);
}
}
// 构造上报信息数据参数
Future<ReportInformationData> _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<ListenAddrData> 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<List<ListenAddrData>> _makeListenAddrDataList() async {
final List<ListenAddrData> listenAddrDataList = [];
final List<String> 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<List<String>> _getAllIpAddresses() async {
final List<String> ipAddresses = [];
try {
final List<NetworkInterface> 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<Map<String, String>> _getDeviceInfo() async {
final Map<String, String> deviceInfo =
await DeviceInfoUtils.getDeviceInfo();
return deviceInfo;
}
/// 解析 UDP URL 并提取 IP 地址和端口号
Map<String, dynamic> _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<int> bytes) {
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join('');
}
// 生成签名sing
Future<String> _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<String> getPublicKey() async {
// 从缓存中获取星图注册节点信息
final StarChartRegisterNodeEntity? starChartRegisterNodeInfo =
await Storage.getStarChartRegisterNodeInfo();
return starChartRegisterNodeInfo?.peer?.publicKey ?? '';
}
Future<String> getPrivateKey() async {
// 从缓存中获取星图注册节点信息
final StarChartRegisterNodeEntity? starChartRegisterNodeInfo =
await Storage.getStarChartRegisterNodeInfo();
return starChartRegisterNodeInfo?.peer?.privateKey ?? '';
}
}