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

消息序列化(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,407 @@
/**
* WebSocket传输层服务端实现
*/
import WebSocket, { WebSocketServer } from 'ws';
import { createLogger, Core } from '@esengine/ecs-framework';
import {
ITransport,
ITransportClientInfo,
ITransportConfig,
ConnectionState,
EventEmitter
} from '@esengine/network-shared';
import * as crypto from 'crypto';
/**
* WebSocket传输层实现
*/
export class WebSocketTransport extends EventEmitter implements ITransport {
private logger = createLogger('WebSocketTransport');
private server?: WebSocketServer;
private clients: Map<string, WebSocket> = new Map();
private clientInfo: Map<string, ITransportClientInfo> = new Map();
private config: ITransportConfig;
private isRunning = false;
/**
* 连接事件处理器
*/
private connectHandlers: ((clientInfo: ITransportClientInfo) => void)[] = [];
/**
* 断开连接事件处理器
*/
private disconnectHandlers: ((clientId: string, reason?: string) => void)[] = [];
/**
* 消息接收事件处理器
*/
private messageHandlers: ((clientId: string, data: ArrayBuffer | string) => void)[] = [];
/**
* 错误事件处理器
*/
private errorHandlers: ((error: Error) => void)[] = [];
/**
* 构造函数
*/
constructor(config: ITransportConfig) {
super();
this.config = {
maxConnections: 1000,
heartbeatInterval: 30000,
connectionTimeout: 60000,
maxMessageSize: 1024 * 1024, // 1MB
compression: true,
...config
};
}
/**
* 启动传输层
*/
async start(port: number, host?: string): Promise<void> {
if (this.isRunning) {
this.logger.warn('WebSocket传输层已在运行');
return;
}
try {
this.server = new WebSocketServer({
port,
host: host || '0.0.0.0',
maxPayload: this.config.maxMessageSize,
perMessageDeflate: this.config.compression ? {
threshold: 1024,
concurrencyLimit: 10,
clientNoContextTakeover: false,
serverNoContextTakeover: false
} : false
});
this.setupServerEvents();
this.isRunning = true;
this.logger.info(`WebSocket服务器已启动: ${host || '0.0.0.0'}:${port}`);
this.logger.info(`最大连接数: ${this.config.maxConnections}`);
this.logger.info(`压缩: ${this.config.compression ? '启用' : '禁用'}`);
} catch (error) {
this.logger.error('启动WebSocket服务器失败:', error);
throw error;
}
}
/**
* 停止传输层
*/
async stop(): Promise<void> {
if (!this.isRunning || !this.server) {
return;
}
return new Promise((resolve) => {
// 断开所有客户端连接
for (const [clientId, ws] of this.clients) {
ws.close(1001, '服务器关闭');
this.handleClientDisconnect(clientId, '服务器关闭');
}
// 关闭服务器
this.server!.close(() => {
this.isRunning = false;
this.server = undefined;
this.logger.info('WebSocket服务器已停止');
resolve();
});
});
}
/**
* 发送数据到指定客户端
*/
send(clientId: string, data: ArrayBuffer | string): void {
const ws = this.clients.get(clientId);
if (!ws || ws.readyState !== WebSocket.OPEN) {
this.logger.warn(`尝试向未连接的客户端发送消息: ${clientId}`);
return;
}
try {
ws.send(data);
} catch (error) {
this.logger.error(`发送消息到客户端 ${clientId} 失败:`, error);
this.handleError(error as Error);
}
}
/**
* 广播数据到所有客户端
*/
broadcast(data: ArrayBuffer | string, exclude?: string[]): void {
const excludeSet = new Set(exclude || []);
for (const [clientId, ws] of this.clients) {
if (excludeSet.has(clientId) || ws.readyState !== WebSocket.OPEN) {
continue;
}
try {
ws.send(data);
} catch (error) {
this.logger.error(`广播消息到客户端 ${clientId} 失败:`, error);
this.handleError(error as Error);
}
}
}
/**
* 监听客户端连接事件
*/
onConnect(handler: (clientInfo: ITransportClientInfo) => void): void {
this.connectHandlers.push(handler);
}
/**
* 监听客户端断开事件
*/
onDisconnect(handler: (clientId: string, reason?: string) => void): void {
this.disconnectHandlers.push(handler);
}
/**
* 监听消息接收事件
*/
onMessage(handler: (clientId: string, data: ArrayBuffer | string) => void): void {
this.messageHandlers.push(handler);
}
/**
* 监听错误事件
*/
onError(handler: (error: Error) => void): void {
this.errorHandlers.push(handler);
}
/**
* 获取连接的客户端数量
*/
getClientCount(): number {
return this.clients.size;
}
/**
* 检查客户端是否连接
*/
isClientConnected(clientId: string): boolean {
const ws = this.clients.get(clientId);
return ws !== undefined && ws.readyState === WebSocket.OPEN;
}
/**
* 断开指定客户端
*/
disconnectClient(clientId: string, reason?: string): void {
const ws = this.clients.get(clientId);
if (ws) {
ws.close(1000, reason || '服务器主动断开');
this.handleClientDisconnect(clientId, reason);
}
}
/**
* 获取客户端信息
*/
getClientInfo(clientId: string): ITransportClientInfo | undefined {
return this.clientInfo.get(clientId);
}
/**
* 获取所有客户端信息
*/
getAllClients(): ITransportClientInfo[] {
return Array.from(this.clientInfo.values());
}
/**
* 设置服务器事件监听
*/
private setupServerEvents(): void {
if (!this.server) return;
this.server.on('connection', (ws, request) => {
this.handleNewConnection(ws, request);
});
this.server.on('error', (error) => {
this.logger.error('WebSocket服务器错误:', error);
this.handleError(error);
});
this.server.on('close', () => {
this.logger.info('WebSocket服务器已关闭');
});
}
/**
* 处理新客户端连接
*/
private handleNewConnection(ws: WebSocket, request: any): void {
// 检查连接数限制
if (this.clients.size >= this.config.maxConnections!) {
this.logger.warn('达到最大连接数限制,拒绝新连接');
ws.close(1013, '服务器繁忙');
return;
}
const clientId = crypto.randomUUID();
const clientInfo: ITransportClientInfo = {
id: clientId,
remoteAddress: request.socket.remoteAddress || 'unknown',
connectTime: Date.now(),
userAgent: request.headers['user-agent'],
headers: request.headers
};
// 存储客户端连接和信息
this.clients.set(clientId, ws);
this.clientInfo.set(clientId, clientInfo);
// 设置WebSocket事件监听
this.setupClientEvents(ws, clientId);
this.logger.info(`新客户端连接: ${clientId} from ${clientInfo.remoteAddress}`);
// 触发连接事件
this.connectHandlers.forEach(handler => {
try {
handler(clientInfo);
} catch (error) {
this.logger.error('连接事件处理器错误:', error);
}
});
}
/**
* 设置客户端WebSocket事件监听
*/
private setupClientEvents(ws: WebSocket, clientId: string): void {
// 消息接收
ws.on('message', (data) => {
this.handleClientMessage(clientId, data);
});
// 连接关闭
ws.on('close', (code, reason) => {
this.handleClientDisconnect(clientId, reason?.toString() || `Code: ${code}`);
});
// 错误处理
ws.on('error', (error) => {
this.logger.error(`客户端 ${clientId} WebSocket错误:`, error);
this.handleError(error);
});
// Pong响应心跳
ws.on('pong', () => {
// 记录客户端响应心跳
const info = this.clientInfo.get(clientId);
if (info) {
// 可以更新延迟信息
}
});
// 设置连接超时
if (this.config.connectionTimeout) {
Core.schedule(this.config.connectionTimeout / 1000, false, this, () => {
if (ws.readyState === WebSocket.OPEN) {
ws.ping();
}
});
}
}
/**
* 处理客户端消息
*/
private handleClientMessage(clientId: string, data: WebSocket.Data): void {
try {
const message = data instanceof ArrayBuffer ? data : new TextEncoder().encode(data.toString()).buffer;
// 触发消息事件
this.messageHandlers.forEach(handler => {
try {
handler(clientId, message);
} catch (error) {
this.logger.error('消息事件处理器错误:', error);
}
});
} catch (error) {
this.logger.error(`处理客户端 ${clientId} 消息失败:`, error);
this.handleError(error as Error);
}
}
/**
* 处理客户端断开连接
*/
private handleClientDisconnect(clientId: string, reason?: string): void {
// 清理客户端数据
this.clients.delete(clientId);
this.clientInfo.delete(clientId);
this.logger.info(`客户端断开连接: ${clientId}, 原因: ${reason || '未知'}`);
// 触发断开连接事件
this.disconnectHandlers.forEach(handler => {
try {
handler(clientId, reason);
} catch (error) {
this.logger.error('断开连接事件处理器错误:', error);
}
});
}
/**
* 处理错误
*/
private handleError(error: Error): void {
this.errorHandlers.forEach(handler => {
try {
handler(error);
} catch (handlerError) {
this.logger.error('错误事件处理器错误:', handlerError);
}
});
}
/**
* 发送心跳到所有客户端
*/
public sendHeartbeat(): void {
for (const [clientId, ws] of this.clients) {
if (ws.readyState === WebSocket.OPEN) {
try {
ws.ping();
} catch (error) {
this.logger.error(`发送心跳到客户端 ${clientId} 失败:`, error);
}
}
}
}
/**
* 获取传输层统计信息
*/
public getStats() {
return {
isRunning: this.isRunning,
clientCount: this.clients.size,
maxConnections: this.config.maxConnections,
compressionEnabled: this.config.compression,
clients: this.getAllClients()
};
}
}