fix:调整新的中继协议中的上报签名逻辑
This commit is contained in:
parent
8c75a8db1c
commit
063fc90a29
@ -239,7 +239,6 @@ class _StarLockLoginPageState extends State<StarLockLoginPage> {
|
||||
await StartChartManage().init();
|
||||
},
|
||||
),
|
||||
|
||||
SubmitBtn(
|
||||
btnName: '发送回声测试消息',
|
||||
onClick: () {
|
||||
|
||||
@ -68,4 +68,6 @@ class StartChartApi extends BaseProvider {
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -5,12 +5,11 @@ import 'package:star_lock/talk/startChart/constant/protocol_flag_constant.dart';
|
||||
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
|
||||
|
||||
class MessageCommand {
|
||||
|
||||
/// 客户端去中继上线命令
|
||||
static List<int> goOnlineRelay({
|
||||
required String FromPeerId,
|
||||
required String ToPeerId,
|
||||
}) {
|
||||
}) {
|
||||
String serializedBytesString = ScpMessage(
|
||||
ProtocolFlag: ProtocolFlagConstant.scp01,
|
||||
MessageType: MessageTypeConstant.Req,
|
||||
@ -68,7 +67,6 @@ class MessageCommand {
|
||||
PayloadLength: 5,
|
||||
PayloadType: PayloadTypeConstant.heartbeat,
|
||||
);
|
||||
|
||||
String serializedBytesString = message.serialize();
|
||||
return _hexToBytes(serializedBytesString);
|
||||
}
|
||||
@ -82,7 +80,7 @@ class MessageCommand {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int calculationCrc(payload){
|
||||
static int calculationCrc(payload) {
|
||||
var checkSumResult = Crc32.calculate(payload);
|
||||
return checkSumResult;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
class IpConstant {
|
||||
// 上报时需要排除的ip
|
||||
static const List<String> reportExcludeIp = ['127.0.0.1','::1%1'];
|
||||
static const List<String> reportExcludeIp = ['127.0.0.1','::1%1','::1'];
|
||||
static const String udpUrl = 'udp://';
|
||||
static const String tcpUrl = 'tcp://';
|
||||
static const String httpsUrl = 'https://';
|
||||
|
||||
@ -1,10 +1,18 @@
|
||||
class PayloadTypeConstant {
|
||||
// 上线
|
||||
static const int goOnline = 100;
|
||||
|
||||
// 回声测试
|
||||
static const int echoTest = 8;
|
||||
|
||||
// 心跳
|
||||
static const int heartbeat = 110;
|
||||
|
||||
// UDP协议的SCD发现服务器查询中继信息
|
||||
static const int query = 120;
|
||||
}
|
||||
|
||||
// 登录成功
|
||||
static const int loginSuccessResponse = 110;
|
||||
// 心跳响应成功
|
||||
static const int heartHeatSuccessResponse = 0;
|
||||
}
|
||||
|
||||
@ -1,39 +1,37 @@
|
||||
class HeartbeatResponse {
|
||||
int statusCode; // 状态码,1字节无符号
|
||||
int nextPingTime; // 下次ping时间秒数,2字节无符号
|
||||
int? statusCode;
|
||||
int? nextPingTime;
|
||||
|
||||
HeartbeatResponse({required this.statusCode, required this.nextPingTime});
|
||||
HeartbeatResponse({
|
||||
this.statusCode,
|
||||
this.nextPingTime,
|
||||
});
|
||||
|
||||
factory HeartbeatResponse.fromBytes(List<int> bytes) {
|
||||
final message = HeartbeatResponse();
|
||||
int offset = 0;
|
||||
|
||||
// Set default value for statusCode
|
||||
message.statusCode = 0; // 或者其他默认值
|
||||
|
||||
// Check if the entire array has at least 2 bytes for nextPingTime
|
||||
if (bytes.length < 2) {
|
||||
throw FormatException("Insufficient data for HeartbeatResponse");
|
||||
}
|
||||
|
||||
// nextPingTime (2 bytes, little-endian)
|
||||
if (bytes.length - offset >= 2) {
|
||||
message.nextPingTime = (bytes[offset + 1] << 8) | bytes[offset];
|
||||
offset += 2;
|
||||
} else {
|
||||
throw FormatException("Invalid nextPingTime length");
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'HeartbeatResponse{statusCode: $statusCode, nextPingTime: $nextPingTime}';
|
||||
}
|
||||
|
||||
// 反序列化方法
|
||||
static HeartbeatResponse deserialize(List<int> bytes) {
|
||||
if (bytes.length < 3) {
|
||||
throw FormatException("Invalid HeartbeatResponse length");
|
||||
}
|
||||
|
||||
final response = HeartbeatResponse(
|
||||
statusCode: bytes[0], // 状态码,1字节
|
||||
nextPingTime: (bytes[2] << 8) | bytes[1], // 下次ping时间,2字节小端序
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// 序列化方法
|
||||
static List<int> serialize(HeartbeatResponse response) {
|
||||
final List<int> bytes = [];
|
||||
|
||||
// 序列化状态码
|
||||
bytes.add(response.statusCode);
|
||||
|
||||
// 序列化下次ping时间(2字节小端序)
|
||||
bytes.add(response.nextPingTime & 0xFF);
|
||||
bytes.add((response.nextPingTime >> 8) & 0xFF);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
59
lib/talk/startChart/entity/login_response.dart
Normal file
59
lib/talk/startChart/entity/login_response.dart
Normal file
@ -0,0 +1,59 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class LoginResponse {
|
||||
int? responseType;
|
||||
int? rejectType;
|
||||
int? nextPingTime;
|
||||
String? clientAddr;
|
||||
|
||||
LoginResponse({
|
||||
this.responseType,
|
||||
this.rejectType,
|
||||
this.nextPingTime,
|
||||
this.clientAddr,
|
||||
});
|
||||
|
||||
factory LoginResponse.fromBytes(List<int> bytes) {
|
||||
final message = LoginResponse();
|
||||
int offset = 0;
|
||||
|
||||
// responseType (1 byte)
|
||||
if (bytes.length - offset >= 1) {
|
||||
message.responseType = bytes[offset];
|
||||
offset += 1;
|
||||
} else {
|
||||
throw FormatException("Invalid responseType length");
|
||||
}
|
||||
|
||||
// rejectType (1 byte)
|
||||
if (bytes.length - offset >= 1) {
|
||||
message.rejectType = bytes[offset];
|
||||
offset += 1;
|
||||
} else {
|
||||
throw FormatException("Invalid rejectType length");
|
||||
}
|
||||
|
||||
// nextPingTime (2 bytes, little-endian)
|
||||
if (bytes.length - offset >= 2) {
|
||||
message.nextPingTime = (bytes[offset + 1] << 8) | bytes[offset];
|
||||
offset += 2;
|
||||
} else {
|
||||
throw FormatException("Invalid nextPingTime length");
|
||||
}
|
||||
|
||||
// ClientAddr (remaining bytes)
|
||||
if (bytes.length > offset) {
|
||||
message.clientAddr =
|
||||
utf8.decode(bytes.sublist(offset).takeWhile((byte) => byte != 0).toList());
|
||||
} else {
|
||||
throw FormatException("Invalid ClientAddr length");
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LoginResponse{responseType: $responseType, rejectType: $rejectType, nextPingTime: $nextPingTime, clientAddr: $clientAddr}';
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import 'package:crypto/crypto.dart';
|
||||
import 'package:star_lock/app_settings/app_settings.dart';
|
||||
import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart';
|
||||
import 'package:star_lock/talk/startChart/entity/heartbeat_response.dart';
|
||||
import 'package:star_lock/talk/startChart/entity/login_response.dart';
|
||||
|
||||
class ScpMessage {
|
||||
ScpMessage({
|
||||
@ -223,9 +224,6 @@ class ScpMessage {
|
||||
|
||||
// PayloadLength (4 bytes, little-endian)
|
||||
if (bytes.length - offset >= 4) {
|
||||
// 打印PayloadLength对应的4个字节
|
||||
print('PayloadLength bytes: ${bytes.sublist(offset, offset + 4)}');
|
||||
|
||||
message.PayloadLength = (bytes[offset] |
|
||||
(bytes[offset + 1] << 8) |
|
||||
(bytes[offset + 2] << 16) |
|
||||
@ -235,75 +233,25 @@ class ScpMessage {
|
||||
throw FormatException("Invalid PayloadLength length");
|
||||
}
|
||||
|
||||
// Payload (字符串,转换为字节)
|
||||
// if (message.PayloadLength != null &&
|
||||
// bytes.length - offset >= message.PayloadLength!) {
|
||||
// message.Payload =
|
||||
// utf8.decode(bytes.sublist(offset, offset + message.PayloadLength!));
|
||||
// offset += message.PayloadLength!;
|
||||
// } else {
|
||||
// throw FormatException("Invalid Payload or PayloadLength");
|
||||
// }
|
||||
|
||||
// 打印解析后的 PayloadLength
|
||||
print('Parsed PayloadLength: ${message.PayloadLength}');
|
||||
|
||||
// 解析Payload
|
||||
if (message.PayloadType == PayloadTypeConstant.heartbeat) {
|
||||
// 假设110表示HeartbeatResponse类型
|
||||
if (message.PayloadLength != null &&
|
||||
bytes.length - offset >= message.PayloadLength!) {
|
||||
final payloadBytes =
|
||||
bytes.sublist(offset, offset + message.PayloadLength!);
|
||||
message.Payload = HeartbeatResponse.deserialize(payloadBytes);
|
||||
offset += message.PayloadLength!;
|
||||
} else {
|
||||
throw FormatException("Invalid Payload or PayloadLength");
|
||||
}
|
||||
// 处理其他类型的Payload
|
||||
if (message.PayloadLength != null &&
|
||||
bytes.length - offset >= message.PayloadLength!) {
|
||||
final sublist = bytes.sublist(offset, offset + message.PayloadLength!);
|
||||
// print('sublist:$sublist');
|
||||
offset += message.PayloadLength!;
|
||||
message.Payload = _handlePayLoad(
|
||||
payloadType: message.PayloadType ?? 0,
|
||||
byte: sublist,
|
||||
offset: offset,
|
||||
PayloadLength: message.PayloadLength,
|
||||
);
|
||||
} else {
|
||||
// 处理其他类型的Payload
|
||||
if (message.PayloadLength != null &&
|
||||
bytes.length - offset >= message.PayloadLength!) {
|
||||
message.Payload =
|
||||
utf8.decode(bytes.sublist(offset, offset + message.PayloadLength!));
|
||||
offset += message.PayloadLength!;
|
||||
} else {
|
||||
throw FormatException("Invalid Payload or PayloadLength");
|
||||
}
|
||||
throw FormatException("Invalid Payload or PayloadLength");
|
||||
}
|
||||
|
||||
// 验证PayloadCRC
|
||||
// if (message.Payload != null) {
|
||||
// var crcBytes = List<int>.from(utf8.encode(message.Payload!));
|
||||
// var calculatedCrc = _calculateCrc16(crcBytes);
|
||||
// if (calculatedCrc != message.PayloadCRC) {
|
||||
// throw FormatException("PayloadCRC verification failed. Expected: ${message.PayloadCRC}, Actual: $calculatedCrc");
|
||||
// }
|
||||
// }
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
// CRC-16 计算函数(示例实现,可能需要根据具体协议调整)
|
||||
static int _calculateCrc16(List<int> data) {
|
||||
const poly = 0x8005;
|
||||
int crc = 0xFFFF;
|
||||
|
||||
for (final b in data) {
|
||||
crc ^= b << 8;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ((crc & 0x8000) != 0) {
|
||||
crc = (crc << 1) ^ poly;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
crc &= 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
static String bytesToHex(List<int> bytes) {
|
||||
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join('');
|
||||
}
|
||||
@ -317,4 +265,31 @@ class ScpMessage {
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// 根据不同payloadType序列化对应的payload结构体
|
||||
static dynamic _handlePayLoad({
|
||||
required int payloadType,
|
||||
required List<int> byte,
|
||||
int? offset,
|
||||
int? PayloadLength,
|
||||
}) {
|
||||
switch (payloadType) {
|
||||
case PayloadTypeConstant.goOnline:
|
||||
// 上线
|
||||
LoginResponse loginResp = LoginResponse.fromBytes(byte);
|
||||
return loginResp;
|
||||
case PayloadTypeConstant.heartbeat:
|
||||
// 心跳
|
||||
HeartbeatResponse heartbeatResponse = HeartbeatResponse.fromBytes(byte);
|
||||
return heartbeatResponse;
|
||||
case PayloadTypeConstant.echoTest:
|
||||
// 回声测试
|
||||
String payload = utf8.decode(byte);
|
||||
return payload;
|
||||
default:
|
||||
print('❌未知的payloadType类型,按照字符串解析');
|
||||
String payload = utf8.decode(byte);
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:convert/convert.dart';
|
||||
import 'package:cryptography/cryptography.dart';
|
||||
import 'package:encrypt/encrypt.dart';
|
||||
import 'package:fast_rsa/fast_rsa.dart' as fastRsa;
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pointycastle/asn1/asn1_parser.dart';
|
||||
import 'package:pointycastle/asn1/primitives/asn1_integer.dart';
|
||||
@ -19,6 +20,9 @@ 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/constant/payload_type_constant.dart';
|
||||
import 'package:star_lock/talk/startChart/entity/heartbeat_response.dart';
|
||||
import 'package:star_lock/talk/startChart/entity/login_response.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/scp_message.dart';
|
||||
@ -54,7 +58,7 @@ class StartChartManage {
|
||||
final int localPort = 62289; // 本地端口
|
||||
String localPublicHost = ''; // 本地公网ip地址
|
||||
|
||||
int heartbeatIntervalTime = 1; // 心跳包间隔时间(s)
|
||||
int _heartbeatIntervalTime = 1; // 心跳包间隔时间(s)
|
||||
Timer? _heartBeatTimer; // 心跳包定时器
|
||||
bool _heartBeatTimerRunning = false; // 心跳包定时任务发送状态
|
||||
|
||||
@ -64,8 +68,19 @@ class StartChartManage {
|
||||
// echo测试peer对端
|
||||
final String echoPeerId = '3phX8Ng2cZHz5NtP8xAf6nYy2z1BYytoejgjoHrWMGhH';
|
||||
|
||||
bool _isOnlineStartChartServer = false; // 星图是否上线成功
|
||||
int _reStartOnlineStartChartServerIntervalTime = 1; // 重新上线星图服务任务间隔(s)
|
||||
Timer? _reStartOnlineStartChartServerTimer; // 重新上线定时器
|
||||
bool _reStartOnlineStartChartServerTimerRunning = false; // 重新上线定时任务发送状态
|
||||
|
||||
String relayPeerId = ''; // 中继peerId
|
||||
|
||||
// 星图服务初始化
|
||||
Future<void> init() async {
|
||||
if (_isOnlineStartChartServer && _udpSocket != null) {
|
||||
// 如果已经上线就不进行初始化
|
||||
return;
|
||||
}
|
||||
// 节点注册
|
||||
await _clientRegister();
|
||||
|
||||
@ -104,6 +119,7 @@ class StartChartManage {
|
||||
final parseUdpUrl = _parseUdpUrl(data?.listenAddr ?? '');
|
||||
remoteHost = parseUdpUrl['host'] ?? '';
|
||||
remotePort = parseUdpUrl['port'] ?? '';
|
||||
relayPeerId = data?.peerID ?? '';
|
||||
}
|
||||
_log(text: '中继信息----》${relayInfoEntity}');
|
||||
}
|
||||
@ -137,12 +153,14 @@ class StartChartManage {
|
||||
if (event == RawSocketEvent.read) {
|
||||
Datagram? dg = socket.receive();
|
||||
try {
|
||||
_log(text: '收到消息---> 长度:${dg?.data?.length}, 数据:${dg?.data}');
|
||||
if (dg?.data != null) {
|
||||
_log(text: '=============${bytesToHex(dg!.data)}');
|
||||
final deserialize = ScpMessage.deserialize(dg!.data);
|
||||
|
||||
_log(text: 'Udp收到结构体数据---》$deserialize');
|
||||
if (deserialize != null) {
|
||||
_handleUdpResultData(deserialize);
|
||||
}
|
||||
if (deserialize.PayloadType != PayloadTypeConstant.heartbeat) {
|
||||
_log(text: 'Udp收到结构体数据---》$deserialize');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
_log(text: '❌ Udp ----> $e');
|
||||
@ -160,43 +178,36 @@ class StartChartManage {
|
||||
reportInformationData: data,
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
_log(text: '星图登录成功');
|
||||
// 发送送上线消息
|
||||
await _sendOnlineMessage();
|
||||
// 发送心跳消息
|
||||
_sendHeartbeatMessage();
|
||||
// 发送送上线消息
|
||||
await _reStartOnlineStartChartServer();
|
||||
}
|
||||
}
|
||||
|
||||
// 发送上线消息
|
||||
Future<void> _sendOnlineMessage() async {
|
||||
if (_isOnlineStartChartServer) {
|
||||
_log(text: '星图已上线,请勿重复发送上线消息');
|
||||
return;
|
||||
}
|
||||
// 组装上线消息
|
||||
final message = MessageCommand.goOnlineRelay(
|
||||
FromPeerId: FromPeerId, ToPeerId: ToPeerId);
|
||||
await _sendMessage(message: message);
|
||||
}
|
||||
|
||||
// 发送回声测试消息
|
||||
void sendEchoMessage({required String ToPeerId}) async {
|
||||
final message = MessageCommand.echoMessage(
|
||||
ToPeerId: ToPeerId,
|
||||
FromPeerId: FromPeerId,
|
||||
ToPeerId: ToPeerId,
|
||||
);
|
||||
await _sendMessage(message: message);
|
||||
}
|
||||
|
||||
// 发送心跳包消息
|
||||
void _sendHeartbeatMessage() async {
|
||||
// 从缓存中获取中继信息
|
||||
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
|
||||
final String relayPeerId = relayInfoEntity?.relay_list?[0].peerID ?? '';
|
||||
if (_heartBeatTimerRunning) {
|
||||
_log(text: '心跳已经开始了. 请勿重复发送心跳包消息');
|
||||
return;
|
||||
}
|
||||
_heartBeatTimer ??= Timer.periodic(
|
||||
Duration(
|
||||
seconds: heartbeatIntervalTime,
|
||||
seconds: _heartbeatIntervalTime,
|
||||
),
|
||||
(Timer timer) async {
|
||||
final List<int> message = MessageCommand.heartbeatMessage(
|
||||
@ -209,17 +220,49 @@ class StartChartManage {
|
||||
_heartBeatTimerRunning = true;
|
||||
}
|
||||
|
||||
// 发送回声测试消息
|
||||
void sendEchoMessage({required String ToPeerId}) async {
|
||||
final message = MessageCommand.echoMessage(
|
||||
ToPeerId: ToPeerId,
|
||||
FromPeerId: FromPeerId,
|
||||
);
|
||||
await _sendMessage(message: message);
|
||||
}
|
||||
|
||||
// 重新上线
|
||||
Future<void> _reStartOnlineStartChartServer() async {
|
||||
if (_isOnlineStartChartServer) {
|
||||
_log(text: '星图已上线,请勿重复发送上线消息');
|
||||
return;
|
||||
}
|
||||
_reStartOnlineStartChartServerTimer ??= Timer.periodic(
|
||||
Duration(
|
||||
seconds: _reStartOnlineStartChartServerIntervalTime,
|
||||
),
|
||||
(Timer timer) async {
|
||||
// 重新发送上线消息
|
||||
await _sendOnlineMessage();
|
||||
},
|
||||
);
|
||||
_reStartOnlineStartChartServerTimerRunning = true;
|
||||
}
|
||||
|
||||
// 停止定时发送心跳包
|
||||
void stopHeartbeat() {
|
||||
_heartBeatTimer?.cancel();
|
||||
_heartBeatTimer = null; // 清除定时器引用
|
||||
_heartBeatTimerRunning = false;
|
||||
_log(text: '发送心跳包结束');
|
||||
}
|
||||
|
||||
// 停止重新上线
|
||||
void stopReStartOnlineStartChartServer() {
|
||||
_reStartOnlineStartChartServerTimer?.cancel();
|
||||
_reStartOnlineStartChartServerTimer = null; // 清除定时器引用
|
||||
_reStartOnlineStartChartServerTimerRunning = false;
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
Future<void> _sendMessage({required List<int> message}) async {
|
||||
// _log(text: '发送给中继的消息体:${message},序列化之后的数据:【${bytesToHex(message)}】');
|
||||
var result = await _udpSocket?.send(
|
||||
message, InternetAddress(remoteHost), remotePort);
|
||||
if (result != message.length) {
|
||||
@ -340,15 +383,10 @@ class StartChartManage {
|
||||
for (final address in interface.addresses) {
|
||||
// 获取原始 IP 地址
|
||||
String ipAddress = address.address;
|
||||
|
||||
// 解码 URL 编码的字符串
|
||||
ipAddress = Uri.decodeFull(ipAddress);
|
||||
|
||||
// 移除 IPv6 地址中的接口标识符(如果有)
|
||||
if (ipAddress.contains('%')) {
|
||||
ipAddress = ipAddress.split('%').first;
|
||||
}
|
||||
|
||||
// 确保 IP 地址不为空且不在排除列表中
|
||||
if (ipAddress.isNotEmpty &&
|
||||
!IpConstant.reportExcludeIp.contains(ipAddress)) {
|
||||
@ -359,7 +397,7 @@ class StartChartManage {
|
||||
} catch (e) {
|
||||
_log(text: '❌--->获取本机IP时出现错误: $e');
|
||||
}
|
||||
return ipAddresses ?? [];
|
||||
return ipAddresses; // 注意:这里不需要 `?? []`,因为 `ipAddresses` 已经初始化为一个空列表。
|
||||
}
|
||||
|
||||
void _log({required String text}) {
|
||||
@ -436,38 +474,6 @@ class StartChartManage {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// 将字节数组转换为 PEM 格式的公钥
|
||||
String convertToPemPublicKey(List<int> publicKeyBytes,
|
||||
{bool isPKCS8 = true}) {
|
||||
// 将字节数组转换为Base64编码的字符串
|
||||
String base64PublicKey = base64Encode(publicKeyBytes);
|
||||
// 添加PEM格式的头尾标签
|
||||
String pemHeader;
|
||||
String pemFooter;
|
||||
if (isPKCS8) {
|
||||
// 添加PEM格式的头尾标签
|
||||
pemHeader = "-----BEGIN PUBLIC KEY-----";
|
||||
pemFooter = "-----END PUBLIC KEY-----";
|
||||
} else {
|
||||
// 添加PEM格式的头尾标签
|
||||
pemHeader = "-----BEGIN RSA PUBLIC KEY-----";
|
||||
pemFooter = "-----END RSA PUBLIC KEY-----";
|
||||
}
|
||||
|
||||
// 将Base64字符串分行为每行64个字符
|
||||
const lineLength = 64;
|
||||
List<String> lines = [];
|
||||
for (int i = 0; i < base64PublicKey.length; i += lineLength) {
|
||||
int end = (i + lineLength < base64PublicKey.length)
|
||||
? i + lineLength
|
||||
: base64PublicKey.length;
|
||||
lines.add(base64PublicKey.substring(i, end));
|
||||
}
|
||||
|
||||
// 组合成完整的PEM格式字符串
|
||||
return "$pemHeader\n${lines.join('\n')}\n$pemFooter";
|
||||
}
|
||||
|
||||
String convertToPemPrivateKey(List<int> privateKeyBytes,
|
||||
{bool isPKCS8 = true}) {
|
||||
// 将字节数组转换为Base64编码的字符串
|
||||
@ -564,4 +570,57 @@ class StartChartManage {
|
||||
await Storage.getStarChartRegisterNodeInfo();
|
||||
return starChartRegisterNodeInfo?.peer?.privateKey ?? '';
|
||||
}
|
||||
|
||||
// 处理udp返回的数据
|
||||
void _handleUdpResultData(ScpMessage scpMessage) {
|
||||
final int payloadType = scpMessage.PayloadType ?? 0;
|
||||
switch (payloadType) {
|
||||
case PayloadTypeConstant.goOnline:
|
||||
_handleUdpGoOnlineResultData(scpMessage);
|
||||
break;
|
||||
case PayloadTypeConstant.heartbeat:
|
||||
_handleUdpHeartBeatResultData(scpMessage);
|
||||
break;
|
||||
case PayloadTypeConstant.echoTest:
|
||||
_handleUdpEchoTestResultData(scpMessage);
|
||||
break;
|
||||
|
||||
default:
|
||||
_log(text: '❌未知的payloadType类型');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理上线命令udp返回的数据
|
||||
void _handleUdpGoOnlineResultData(ScpMessage scpMessage) {
|
||||
final LoginResponse loginResponse = scpMessage.Payload;
|
||||
final responseType = loginResponse.responseType;
|
||||
if (responseType != null &&
|
||||
responseType == PayloadTypeConstant.loginSuccessResponse) {
|
||||
_log(text: '星图登录成功');
|
||||
_isOnlineStartChartServer = true;
|
||||
// 上线成功,停止重发
|
||||
stopReStartOnlineStartChartServer();
|
||||
} else {
|
||||
// 上线失败,重发
|
||||
_reStartOnlineStartChartServer();
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理心跳命令udp返回的数据
|
||||
void _handleUdpHeartBeatResultData(ScpMessage scpMessage) {
|
||||
final HeartbeatResponse heartbeatResponse = scpMessage.Payload;
|
||||
final statusCode = heartbeatResponse.statusCode;
|
||||
if (statusCode != null &&
|
||||
statusCode != PayloadTypeConstant.heartHeatSuccessResponse) {
|
||||
// 心跳响应失败,需要重新上线
|
||||
_reStartOnlineStartChartServer();
|
||||
}
|
||||
_heartbeatIntervalTime = heartbeatResponse.nextPingTime ?? 1;
|
||||
}
|
||||
|
||||
/// 处理回声测试
|
||||
void _handleUdpEchoTestResultData(ScpMessage scpMessage) {
|
||||
EasyLoading.showToast(scpMessage.Payload, duration: 2000.milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,6 +251,8 @@ dependencies:
|
||||
cryptography: ^2.7.0
|
||||
asn1lib: ^1.0.0
|
||||
fast_rsa: ^3.6.6
|
||||
crc: ^0.0.2
|
||||
crclib: ^3.0.0
|
||||
|
||||
dependency_overrides:
|
||||
#强制设置google_maps_flutter_ios 为 2.5.2
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user