177 lines
4.9 KiB
Dart
177 lines
4.9 KiB
Dart
import 'dart:collection';
|
||
|
||
class PacketLossStatistics {
|
||
static final PacketLossStatistics _instance =
|
||
PacketLossStatistics._internal();
|
||
factory PacketLossStatistics() => _instance;
|
||
PacketLossStatistics._internal();
|
||
|
||
// 记录每个messageId的分包信息
|
||
// key: messageId, value: {totalPackets, receivedPackets}
|
||
final Map<int, PacketInfo> _packetsMap = HashMap();
|
||
|
||
// 配置参数
|
||
int _maxCapacity = 300; // 最大容量为300条记录
|
||
int _timeoutMs = 30000; // 默认超时时间为30秒
|
||
|
||
// 统计信息
|
||
int _totalMessages = 0; // 总消息数
|
||
int _lostMessages = 0; // 丢包的消息数
|
||
int _totalPackets = 0; // 总分包数
|
||
int _lostPackets = 0; // 丢失的分包数
|
||
|
||
// 记录分包数据
|
||
void recordPacket(int messageId, int currentIndex, int totalPackets) {
|
||
// 定期清理超时记录
|
||
_cleanupExpiredPackets();
|
||
|
||
// 检查容量限制
|
||
_checkCapacityLimit();
|
||
|
||
if (!_packetsMap.containsKey(messageId)) {
|
||
_packetsMap[messageId] = PacketInfo(totalPackets);
|
||
_totalMessages++;
|
||
_totalPackets += totalPackets;
|
||
} else {
|
||
// 更新时间戳
|
||
_packetsMap[messageId]!.timestamp = DateTime.now().millisecondsSinceEpoch;
|
||
}
|
||
|
||
_packetsMap[messageId]!.receivedPackets.add(currentIndex);
|
||
|
||
// 如果收到了该messageId的最后一个包,进行统计
|
||
if (currentIndex == totalPackets) {
|
||
_checkPacketLoss(messageId);
|
||
}
|
||
}
|
||
|
||
// 清理超时的记录
|
||
void _cleanupExpiredPackets() {
|
||
final currentTime = DateTime.now().millisecondsSinceEpoch;
|
||
final expiredMessageIds = <int>[];
|
||
|
||
_packetsMap.forEach((messageId, info) {
|
||
// 如果记录超时,添加到待清理列表
|
||
if (currentTime - info.timestamp > _timeoutMs) {
|
||
expiredMessageIds.add(messageId);
|
||
|
||
// 统计丢包
|
||
_lostMessages++;
|
||
_lostPackets += (info.totalPackets - info.receivedPackets.length);
|
||
}
|
||
});
|
||
|
||
// 移除超时记录
|
||
for (var messageId in expiredMessageIds) {
|
||
_packetsMap.remove(messageId);
|
||
}
|
||
}
|
||
|
||
// 检查并确保不超过最大容量
|
||
void _checkCapacityLimit() {
|
||
if (_packetsMap.length <= _maxCapacity) {
|
||
return;
|
||
}
|
||
|
||
// 如果超过容量限制,按时间戳排序并删除最旧的记录
|
||
var entries = _packetsMap.entries.toList()
|
||
..sort((a, b) => a.value.timestamp.compareTo(b.value.timestamp));
|
||
|
||
// 计算需要移除的数量(移除25%的旧记录,至少保证有一定空间)
|
||
int removeCount = (_packetsMap.length * 0.25).ceil();
|
||
|
||
// 移除并统计丢包
|
||
for (int i = 0; i < removeCount && i < entries.length; i++) {
|
||
var entry = entries[i];
|
||
_lostMessages++;
|
||
_lostPackets +=
|
||
(entry.value.totalPackets - entry.value.receivedPackets.length);
|
||
_packetsMap.remove(entry.key);
|
||
}
|
||
}
|
||
|
||
// 检查丢包情况
|
||
void _checkPacketLoss(int messageId) {
|
||
final info = _packetsMap[messageId]!;
|
||
|
||
// 检查是否有丢失的包
|
||
int received = info.receivedPackets.length;
|
||
if (received < info.totalPackets) {
|
||
_lostMessages++;
|
||
_lostPackets += (info.totalPackets - received);
|
||
}
|
||
|
||
// 清理该messageId的记录,避免内存泄漏
|
||
_packetsMap.remove(messageId);
|
||
}
|
||
|
||
// 获取丢包率统计信息
|
||
PacketLossInfo getStatistics() {
|
||
if (_totalMessages == 0 || _totalPackets == 0) {
|
||
return PacketLossInfo(0.0, 0.0);
|
||
}
|
||
|
||
// 计算消息级别的丢包率
|
||
double messageLossRate = (_lostMessages / _totalMessages) * 100;
|
||
|
||
// 计算分包级别的丢包率
|
||
double packetLossRate = (_lostPackets / _totalPackets) * 100;
|
||
|
||
return PacketLossInfo(messageLossRate, packetLossRate);
|
||
}
|
||
|
||
// Getter和Setter,允许外部调整参数
|
||
int get maxCapacity => _maxCapacity;
|
||
set maxCapacity(int value) {
|
||
if (value > 0) {
|
||
_maxCapacity = value;
|
||
// 设置新容量后立即检查
|
||
_checkCapacityLimit();
|
||
}
|
||
}
|
||
|
||
int get timeoutMs => _timeoutMs;
|
||
set timeoutMs(int value) {
|
||
if (value > 0) {
|
||
_timeoutMs = value;
|
||
// 设置新超时后立即清理
|
||
_cleanupExpiredPackets();
|
||
}
|
||
}
|
||
|
||
// 获取当前未完成记录数
|
||
int get pendingRecordsCount => _packetsMap.length;
|
||
|
||
// 重置统计数据
|
||
void reset() {
|
||
_packetsMap.clear();
|
||
_totalMessages = 0;
|
||
_lostMessages = 0;
|
||
_totalPackets = 0;
|
||
_lostPackets = 0;
|
||
}
|
||
}
|
||
|
||
// 分包信息类
|
||
class PacketInfo {
|
||
final int totalPackets;
|
||
final Set<int> receivedPackets = HashSet<int>();
|
||
int timestamp; // 添加时间戳字段,记录最后更新时间
|
||
|
||
PacketInfo(this.totalPackets)
|
||
: timestamp = DateTime.now().millisecondsSinceEpoch;
|
||
}
|
||
|
||
// 丢包统计信息类
|
||
class PacketLossInfo {
|
||
final double messageLossRate; // 消息丢失率
|
||
final double packetLossRate; // 分包丢失率
|
||
|
||
PacketLossInfo(this.messageLossRate, this.packetLossRate);
|
||
|
||
@override
|
||
String toString() {
|
||
return 'Message Loss Rate: ${messageLossRate.toStringAsFixed(2)}%, Packet Loss Rate: ${packetLossRate.toStringAsFixed(2)}%';
|
||
}
|
||
}
|