传输层实现(客户端/服务端,链接管理和心跳机制,重连机制)

消息序列化(json序列化,消息压缩,消息ID和时间戳)
网络服务器核心(networkserver/基础room/链接状态同步)
网络客户端核心(networkclient/消息队列)
This commit is contained in:
YHH
2025-08-14 23:59:00 +08:00
parent 32092f992d
commit 6730a5d625
29 changed files with 8100 additions and 236 deletions

View File

@@ -0,0 +1,502 @@
/**
* 消息管理器
* 负责消息ID生成、时间戳管理和消息验证
*/
import { createLogger } from '@esengine/ecs-framework';
import { INetworkMessage, MessageType } from '../types/NetworkTypes';
/**
* 消息ID生成器类型
*/
export enum MessageIdGeneratorType {
UUID = 'uuid',
SNOWFLAKE = 'snowflake',
SEQUENTIAL = 'sequential',
TIMESTAMP = 'timestamp'
}
/**
* 消息管理器配置
*/
export interface MessageManagerConfig {
idGenerator: MessageIdGeneratorType;
enableTimestampValidation: boolean;
maxTimestampDrift: number; // 最大时间戳偏移(毫秒)
enableMessageDeduplication: boolean;
deduplicationWindowMs: number; // 去重窗口时间
enableMessageOrdering: boolean;
orderingWindowMs: number; // 排序窗口时间
maxPendingMessages: number; // 最大待处理消息数
}
/**
* 消息验证结果
*/
export interface MessageValidationResult {
isValid: boolean;
errors: string[];
warnings: string[];
}
/**
* 消息统计信息
*/
export interface MessageStats {
totalGenerated: number;
totalValidated: number;
validMessages: number;
invalidMessages: number;
duplicateMessages: number;
outOfOrderMessages: number;
timestampErrors: number;
}
/**
* Snowflake ID生成器
*/
class SnowflakeIdGenerator {
private static readonly EPOCH = 1640995200000; // 2022-01-01 00:00:00 UTC
private static readonly WORKER_ID_BITS = 5;
private static readonly DATACENTER_ID_BITS = 5;
private static readonly SEQUENCE_BITS = 12;
private readonly workerId: number;
private readonly datacenterId: number;
private sequence = 0;
private lastTimestamp = -1;
constructor(workerId: number = 1, datacenterId: number = 1) {
this.workerId = workerId & ((1 << SnowflakeIdGenerator.WORKER_ID_BITS) - 1);
this.datacenterId = datacenterId & ((1 << SnowflakeIdGenerator.DATACENTER_ID_BITS) - 1);
}
generate(): string {
let timestamp = Date.now();
if (timestamp < this.lastTimestamp) {
throw new Error('时钟回拨无法生成ID');
}
if (timestamp === this.lastTimestamp) {
this.sequence = (this.sequence + 1) & ((1 << SnowflakeIdGenerator.SEQUENCE_BITS) - 1);
if (this.sequence === 0) {
// 等待下一毫秒
while (timestamp <= this.lastTimestamp) {
timestamp = Date.now();
}
}
} else {
this.sequence = 0;
}
this.lastTimestamp = timestamp;
const id = ((timestamp - SnowflakeIdGenerator.EPOCH) << 22) |
(this.datacenterId << 17) |
(this.workerId << 12) |
this.sequence;
return id.toString();
}
}
/**
* 消息管理器
*/
export class MessageManager {
private logger = createLogger('MessageManager');
private config: MessageManagerConfig;
private stats: MessageStats;
// ID生成器
private sequentialId = 0;
private snowflakeGenerator: SnowflakeIdGenerator;
// 消息去重和排序
private recentMessageIds: Set<string> = new Set();
private pendingMessages: Map<string, INetworkMessage> = new Map();
private messageSequence: Map<string, number> = new Map();
// 清理定时器
private cleanupTimer?: NodeJS.Timeout;
/**
* 构造函数
*/
constructor(config: Partial<MessageManagerConfig> = {}) {
this.config = {
idGenerator: MessageIdGeneratorType.UUID,
enableTimestampValidation: true,
maxTimestampDrift: 60000, // 1分钟
enableMessageDeduplication: true,
deduplicationWindowMs: 300000, // 5分钟
enableMessageOrdering: false,
orderingWindowMs: 10000, // 10秒
maxPendingMessages: 1000,
...config
};
this.stats = {
totalGenerated: 0,
totalValidated: 0,
validMessages: 0,
invalidMessages: 0,
duplicateMessages: 0,
outOfOrderMessages: 0,
timestampErrors: 0
};
this.snowflakeGenerator = new SnowflakeIdGenerator();
this.startCleanupTimer();
}
/**
* 生成消息ID
*/
generateMessageId(): string {
this.stats.totalGenerated++;
switch (this.config.idGenerator) {
case MessageIdGeneratorType.UUID:
return this.generateUUID();
case MessageIdGeneratorType.SNOWFLAKE:
return this.snowflakeGenerator.generate();
case MessageIdGeneratorType.SEQUENTIAL:
return (++this.sequentialId).toString();
case MessageIdGeneratorType.TIMESTAMP:
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
default:
return this.generateUUID();
}
}
/**
* 创建网络消息
*/
createMessage<T extends INetworkMessage>(
type: MessageType,
data: any,
senderId: string,
options: {
reliable?: boolean;
priority?: number;
timestamp?: number;
} = {}
): T {
const message: INetworkMessage = {
type,
messageId: this.generateMessageId(),
timestamp: options.timestamp || Date.now(),
senderId,
data,
reliable: options.reliable,
priority: options.priority
};
return message as T;
}
/**
* 验证消息
*/
validateMessage(message: INetworkMessage, senderId?: string): MessageValidationResult {
this.stats.totalValidated++;
const errors: string[] = [];
const warnings: string[] = [];
// 基础字段验证
if (!message.messageId) {
errors.push('消息ID不能为空');
}
if (!message.type) {
errors.push('消息类型不能为空');
} else if (!Object.values(MessageType).includes(message.type)) {
errors.push(`无效的消息类型: ${message.type}`);
}
if (!message.timestamp) {
errors.push('时间戳不能为空');
}
if (!message.senderId) {
errors.push('发送者ID不能为空');
}
// 发送者验证
if (senderId && message.senderId !== senderId) {
errors.push('消息发送者ID不匹配');
}
// 时间戳验证
if (this.config.enableTimestampValidation && message.timestamp) {
const now = Date.now();
const drift = Math.abs(now - message.timestamp);
if (drift > this.config.maxTimestampDrift) {
errors.push(`时间戳偏移过大: ${drift}ms > ${this.config.maxTimestampDrift}ms`);
this.stats.timestampErrors++;
}
if (message.timestamp > now + 10000) { // 未来10秒以上
warnings.push('消息时间戳来自未来');
}
}
// 消息去重验证
if (this.config.enableMessageDeduplication) {
if (this.recentMessageIds.has(message.messageId)) {
errors.push('重复的消息ID');
this.stats.duplicateMessages++;
} else {
this.recentMessageIds.add(message.messageId);
}
}
const isValid = errors.length === 0;
if (isValid) {
this.stats.validMessages++;
} else {
this.stats.invalidMessages++;
}
return {
isValid,
errors,
warnings
};
}
/**
* 处理消息排序
*/
processMessageOrdering(message: INetworkMessage): INetworkMessage[] {
if (!this.config.enableMessageOrdering) {
return [message];
}
const senderId = message.senderId;
const currentSequence = this.messageSequence.get(senderId) || 0;
// 检查消息是否按顺序到达
const messageTimestamp = message.timestamp;
const expectedSequence = currentSequence + 1;
// 简单的时间戳排序逻辑
if (messageTimestamp >= expectedSequence) {
// 消息按顺序到达
this.messageSequence.set(senderId, messageTimestamp);
return this.flushPendingMessages(senderId).concat([message]);
} else {
// 消息乱序,暂存
this.pendingMessages.set(message.messageId, message);
this.stats.outOfOrderMessages++;
// 检查是否超出最大待处理数量
if (this.pendingMessages.size > this.config.maxPendingMessages) {
this.logger.warn('待处理消息数量过多,清理旧消息');
this.cleanupOldPendingMessages();
}
return [];
}
}
/**
* 获取统计信息
*/
getStats(): MessageStats {
return { ...this.stats };
}
/**
* 重置统计信息
*/
resetStats(): void {
this.stats = {
totalGenerated: 0,
totalValidated: 0,
validMessages: 0,
invalidMessages: 0,
duplicateMessages: 0,
outOfOrderMessages: 0,
timestampErrors: 0
};
}
/**
* 更新配置
*/
updateConfig(newConfig: Partial<MessageManagerConfig>): void {
const oldConfig = { ...this.config };
Object.assign(this.config, newConfig);
// 如果去重配置改变,清理相关数据
if (!this.config.enableMessageDeduplication && oldConfig.enableMessageDeduplication) {
this.recentMessageIds.clear();
}
// 如果排序配置改变,清理相关数据
if (!this.config.enableMessageOrdering && oldConfig.enableMessageOrdering) {
this.pendingMessages.clear();
this.messageSequence.clear();
}
this.logger.info('消息管理器配置已更新:', newConfig);
}
/**
* 销毁管理器
*/
destroy(): void {
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = undefined;
}
this.recentMessageIds.clear();
this.pendingMessages.clear();
this.messageSequence.clear();
}
/**
* 生成UUID
*/
private generateUUID(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
/**
* 刷新待处理消息
*/
private flushPendingMessages(senderId: string): INetworkMessage[] {
const flushedMessages: INetworkMessage[] = [];
const messagesToRemove: string[] = [];
for (const [messageId, message] of this.pendingMessages) {
if (message.senderId === senderId) {
flushedMessages.push(message);
messagesToRemove.push(messageId);
}
}
// 移除已处理的消息
messagesToRemove.forEach(id => this.pendingMessages.delete(id));
// 按时间戳排序
flushedMessages.sort((a, b) => a.timestamp - b.timestamp);
return flushedMessages;
}
/**
* 清理过期的待处理消息
*/
private cleanupOldPendingMessages(): void {
const now = Date.now();
const messagesToRemove: string[] = [];
for (const [messageId, message] of this.pendingMessages) {
if (now - message.timestamp > this.config.orderingWindowMs) {
messagesToRemove.push(messageId);
}
}
messagesToRemove.forEach(id => this.pendingMessages.delete(id));
if (messagesToRemove.length > 0) {
this.logger.debug(`清理了 ${messagesToRemove.length} 个过期的待处理消息`);
}
}
/**
* 启动清理定时器
*/
private startCleanupTimer(): void {
this.cleanupTimer = setInterval(() => {
this.performCleanup();
}, 60000); // 每分钟清理一次
}
/**
* 执行清理操作
*/
private performCleanup(): void {
const now = Date.now();
// 清理过期的消息ID用于去重
if (this.config.enableMessageDeduplication) {
// 由于Set没有时间戳我们定期清理所有ID
// 这是一个简化实现,实际项目中可以使用更复杂的数据结构
if (this.recentMessageIds.size > 10000) {
this.recentMessageIds.clear();
this.logger.debug('清理了过期的消息ID缓存');
}
}
// 清理过期的待处理消息
if (this.config.enableMessageOrdering) {
this.cleanupOldPendingMessages();
}
}
/**
* 获取消息处理报告
*/
getProcessingReport() {
const totalProcessed = this.stats.validMessages + this.stats.invalidMessages;
const validRate = totalProcessed > 0 ? (this.stats.validMessages / totalProcessed) * 100 : 0;
const duplicateRate = totalProcessed > 0 ? (this.stats.duplicateMessages / totalProcessed) * 100 : 0;
return {
stats: this.getStats(),
validationRate: validRate,
duplicateRate: duplicateRate,
pendingMessagesCount: this.pendingMessages.size,
cachedMessageIdsCount: this.recentMessageIds.size,
recommendation: this.generateRecommendation(validRate, duplicateRate)
};
}
/**
* 生成优化建议
*/
private generateRecommendation(validRate: number, duplicateRate: number): string {
if (validRate < 90) {
return '消息验证失败率较高,建议检查消息格式和发送逻辑';
} else if (duplicateRate > 5) {
return '重复消息较多,建议检查客户端重发逻辑或调整去重窗口';
} else if (this.pendingMessages.size > this.config.maxPendingMessages * 0.8) {
return '待处理消息过多,建议优化网络或调整排序窗口';
} else {
return '消息处理正常';
}
}
/**
* 批量验证消息
*/
validateMessageBatch(messages: INetworkMessage[], senderId?: string): MessageValidationResult[] {
return messages.map(message => this.validateMessage(message, senderId));
}
/**
* 获取消息年龄(毫秒)
*/
getMessageAge(message: INetworkMessage): number {
return Date.now() - message.timestamp;
}
/**
* 检查消息是否过期
*/
isMessageExpired(message: INetworkMessage, maxAge: number = 300000): boolean {
return this.getMessageAge(message) > maxAge;
}
}