NetWebSocketClinet
This commit is contained in:
222
client/assets/scripts/Framework/Net/NetConfigBuilder.ts
Normal file
222
client/assets/scripts/Framework/Net/NetConfigBuilder.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
import { NetConfig, NetProtocolType } from './NetConfig';
|
||||
|
||||
/**
|
||||
* 网络配置构建器
|
||||
* 提供便捷的方法来创建网络配置
|
||||
*/
|
||||
export class NetConfigBuilder {
|
||||
private _config: Partial<NetConfig> = {};
|
||||
|
||||
/**
|
||||
* 设置服务器地址
|
||||
* @param serverUrl 服务器地址
|
||||
*/
|
||||
setServerUrl(serverUrl: string): NetConfigBuilder {
|
||||
this._config.serverUrl = serverUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置协议类型
|
||||
* @param protocolType 协议类型
|
||||
*/
|
||||
setProtocolType(protocolType: NetProtocolType): NetConfigBuilder {
|
||||
this._config.protocolType = protocolType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置为 HTTP 协议
|
||||
* @param serverUrl HTTP 服务器地址 (如: https://api.example.com/api)
|
||||
*/
|
||||
useHttp(serverUrl?: string): NetConfigBuilder {
|
||||
this._config.protocolType = NetProtocolType.Http;
|
||||
if (serverUrl) {
|
||||
this._config.serverUrl = serverUrl;
|
||||
}
|
||||
// HTTP 通常不需要重连
|
||||
this._config.autoReconnect = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置为 WebSocket 协议
|
||||
* @param serverUrl WebSocket 服务器地址 (如: wss://ws.example.com/api)
|
||||
*/
|
||||
useWebSocket(serverUrl?: string): NetConfigBuilder {
|
||||
this._config.protocolType = NetProtocolType.WebSocket;
|
||||
if (serverUrl) {
|
||||
this._config.serverUrl = serverUrl;
|
||||
}
|
||||
// WebSocket 默认启用重连
|
||||
this._config.autoReconnect = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置超时时间
|
||||
* @param timeout 超时时间(毫秒)
|
||||
*/
|
||||
setTimeout(timeout: number): NetConfigBuilder {
|
||||
this._config.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置重连配置
|
||||
* @param autoReconnect 是否自动重连
|
||||
* @param reconnectInterval 重连间隔(毫秒)
|
||||
* @param maxReconnectTimes 最大重连次数
|
||||
*/
|
||||
setReconnect(autoReconnect: boolean, reconnectInterval?: number, maxReconnectTimes?: number): NetConfigBuilder {
|
||||
this._config.autoReconnect = autoReconnect;
|
||||
if (reconnectInterval !== undefined) {
|
||||
this._config.reconnectInterval = reconnectInterval;
|
||||
}
|
||||
if (maxReconnectTimes !== undefined) {
|
||||
this._config.maxReconnectTimes = maxReconnectTimes;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否使用JSON格式
|
||||
* @param json 是否使用JSON格式
|
||||
*/
|
||||
setJson(json: boolean): NetConfigBuilder {
|
||||
this._config.json = json;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建配置
|
||||
*/
|
||||
build(): NetConfig {
|
||||
if (!this._config.serverUrl) {
|
||||
throw new Error('Server URL is required');
|
||||
}
|
||||
|
||||
return {
|
||||
serverUrl: this._config.serverUrl,
|
||||
protocolType: this._config.protocolType || NetProtocolType.Http,
|
||||
timeout: this._config.timeout || 30000,
|
||||
autoReconnect: this._config.autoReconnect ?? true,
|
||||
reconnectInterval: this._config.reconnectInterval || 3000,
|
||||
maxReconnectTimes: this._config.maxReconnectTimes || 5,
|
||||
json: this._config.json ?? true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新的构建器实例
|
||||
*/
|
||||
static create(): NetConfigBuilder {
|
||||
return new NetConfigBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预定义的网络配置模板
|
||||
*/
|
||||
export class NetConfigTemplates {
|
||||
/**
|
||||
* 开发环境 HTTP 配置
|
||||
*/
|
||||
static developmentHttp(serverUrl: string): NetConfig {
|
||||
return NetConfigBuilder.create()
|
||||
.useHttp(serverUrl)
|
||||
.setTimeout(10000)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生产环境 HTTP 配置
|
||||
*/
|
||||
static productionHttp(serverUrl: string): NetConfig {
|
||||
return NetConfigBuilder.create()
|
||||
.useHttp(serverUrl)
|
||||
.setTimeout(30000)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开发环境 WebSocket 配置
|
||||
*/
|
||||
static developmentWebSocket(serverUrl: string): NetConfig {
|
||||
return NetConfigBuilder.create()
|
||||
.useWebSocket(serverUrl)
|
||||
.setTimeout(10000)
|
||||
.setReconnect(true, 1000, 10) // 更频繁的重连用于开发
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生产环境 WebSocket 配置
|
||||
*/
|
||||
static productionWebSocket(serverUrl: string): NetConfig {
|
||||
return NetConfigBuilder.create()
|
||||
.useWebSocket(serverUrl)
|
||||
.setTimeout(30000)
|
||||
.setReconnect(true, 3000, 5)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 游戏实时对战 WebSocket 配置
|
||||
*/
|
||||
static gameRealtimeWebSocket(serverUrl: string): NetConfig {
|
||||
return NetConfigBuilder.create()
|
||||
.useWebSocket(serverUrl)
|
||||
.setTimeout(5000) // 短超时
|
||||
.setReconnect(true, 500, 20) // 快速重连
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络环境检测工具
|
||||
*/
|
||||
export class NetEnvironmentDetector {
|
||||
/**
|
||||
* 根据当前环境自动生成配置
|
||||
* @param baseUrl 基础URL (不包含协议)
|
||||
* @param isProduction 是否为生产环境
|
||||
*/
|
||||
static autoDetect(baseUrl: string, isProduction: boolean = false): {
|
||||
http: NetConfig;
|
||||
webSocket: NetConfig;
|
||||
} {
|
||||
const httpUrl = isProduction ? `https://${baseUrl}/api` : `http://${baseUrl}/api`;
|
||||
const wsUrl = isProduction ? `wss://${baseUrl}/api` : `ws://${baseUrl}/api`;
|
||||
|
||||
return {
|
||||
http: isProduction
|
||||
? NetConfigTemplates.productionHttp(httpUrl)
|
||||
: NetConfigTemplates.developmentHttp(httpUrl),
|
||||
webSocket: isProduction
|
||||
? NetConfigTemplates.productionWebSocket(wsUrl)
|
||||
: NetConfigTemplates.developmentWebSocket(wsUrl)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测URL是否为安全连接
|
||||
*/
|
||||
static isSecureUrl(url: string): boolean {
|
||||
return url.startsWith('https://') || url.startsWith('wss://');
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换HTTP URL为WebSocket URL
|
||||
*/
|
||||
static httpToWebSocket(httpUrl: string): string {
|
||||
return httpUrl.replace(/^https?:/, httpUrl.startsWith('https:') ? 'wss:' : 'ws:');
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换WebSocket URL为HTTP URL
|
||||
*/
|
||||
static webSocketToHttp(wsUrl: string): string {
|
||||
return wsUrl.replace(/^wss?:/, wsUrl.startsWith('wss:') ? 'https:' : 'http:');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "3b215a9a-fd5d-404a-8254-dd6f3cffca6a",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
237
client/assets/scripts/Framework/Net/WebSocketClient.ts
Normal file
237
client/assets/scripts/Framework/Net/WebSocketClient.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
import { BaseServiceType } from 'tsrpc-browser';
|
||||
import { NetEvent } from './NetEvent';
|
||||
|
||||
/**
|
||||
* WebSocket 连接状态
|
||||
*/
|
||||
export enum WsConnectionStatus {
|
||||
/** 已断开 */
|
||||
Disconnected = "disconnected",
|
||||
/** 连接中 */
|
||||
Connecting = "connecting",
|
||||
/** 已连接 */
|
||||
Connected = "connected",
|
||||
/** 连接失败 */
|
||||
Failed = "failed"
|
||||
}
|
||||
|
||||
/**
|
||||
* WebSocket 客户端接口
|
||||
* 统一 HTTP 和 WebSocket 客户端的接口
|
||||
*/
|
||||
export interface INetClient<T extends BaseServiceType = any> {
|
||||
/** 调用API */
|
||||
callApi<Req, Res>(apiName: string, req: Req): Promise<{ isSucc: boolean; res?: Res; err?: any }>;
|
||||
|
||||
/** 监听消息 (仅 WebSocket) */
|
||||
listenMsg?(msgName: string, handler: Function): void;
|
||||
|
||||
/** 取消监听消息 (仅 WebSocket) */
|
||||
unlistenMsg?(msgName: string, handler?: Function): void;
|
||||
|
||||
/** 发送消息 (仅 WebSocket) */
|
||||
sendMsg?(msgName: string, msg: any): void;
|
||||
|
||||
/** 连接 (仅 WebSocket) */
|
||||
connect?(): Promise<{ isSucc: boolean; errMsg?: string }>;
|
||||
|
||||
/** 断开连接 (仅 WebSocket) */
|
||||
disconnect?(): void;
|
||||
|
||||
/** 获取连接状态 */
|
||||
status?: WsConnectionStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebSocket 客户端包装器
|
||||
* 提供统一的接口和事件处理
|
||||
*/
|
||||
export class WebSocketClientWrapper<T extends BaseServiceType = any> implements INetClient<T> {
|
||||
private _wsClient: any = null;
|
||||
private _status: WsConnectionStatus = WsConnectionStatus.Disconnected;
|
||||
private _eventCallbacks: Map<string, Function[]> = new Map();
|
||||
|
||||
constructor(wsClient: any) {
|
||||
this._wsClient = wsClient;
|
||||
this.setupEventHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置事件处理器
|
||||
*/
|
||||
private setupEventHandlers(): void {
|
||||
if (!this._wsClient) return;
|
||||
|
||||
// 监听连接事件
|
||||
this._wsClient.flows.preConnectFlow.push((v: any) => {
|
||||
this._status = WsConnectionStatus.Connecting;
|
||||
this.emit(NetEvent.Connecting);
|
||||
return v;
|
||||
});
|
||||
|
||||
this._wsClient.flows.postConnectFlow.push((v: any) => {
|
||||
if (v.isSucc) {
|
||||
this._status = WsConnectionStatus.Connected;
|
||||
this.emit(NetEvent.Connected);
|
||||
} else {
|
||||
this._status = WsConnectionStatus.Failed;
|
||||
this.emit(NetEvent.Error, v.errMsg);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
// 监听断开连接事件
|
||||
this._wsClient.flows.postDisconnectFlow.push((v: any) => {
|
||||
this._status = WsConnectionStatus.Disconnected;
|
||||
this.emit(NetEvent.Disconnected);
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接服务器
|
||||
*/
|
||||
async connect(): Promise<{ isSucc: boolean; errMsg?: string }> {
|
||||
try {
|
||||
this._status = WsConnectionStatus.Connecting;
|
||||
const result = await this._wsClient.connect();
|
||||
|
||||
if (result.isSucc) {
|
||||
this._status = WsConnectionStatus.Connected;
|
||||
} else {
|
||||
this._status = WsConnectionStatus.Failed;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
this._status = WsConnectionStatus.Failed;
|
||||
return {
|
||||
isSucc: false,
|
||||
errMsg: error instanceof Error ? error.message : String(error)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开连接
|
||||
*/
|
||||
disconnect(): void {
|
||||
if (this._wsClient && typeof this._wsClient.disconnect === 'function') {
|
||||
this._wsClient.disconnect();
|
||||
}
|
||||
this._status = WsConnectionStatus.Disconnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用API
|
||||
*/
|
||||
async callApi<Req, Res>(apiName: string, req: Req): Promise<{ isSucc: boolean; res?: Res; err?: any }> {
|
||||
if (this._status !== WsConnectionStatus.Connected) {
|
||||
return {
|
||||
isSucc: false,
|
||||
err: 'WebSocket not connected'
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
return await this._wsClient.callApi(apiName, req);
|
||||
} catch (error) {
|
||||
return {
|
||||
isSucc: false,
|
||||
err: error
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听消息
|
||||
*/
|
||||
listenMsg(msgName: string, handler: Function): void {
|
||||
if (this._wsClient && typeof this._wsClient.listenMsg === 'function') {
|
||||
this._wsClient.listenMsg(msgName, handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消监听消息
|
||||
*/
|
||||
unlistenMsg(msgName: string, handler?: Function): void {
|
||||
if (this._wsClient && typeof this._wsClient.unlistenMsg === 'function') {
|
||||
this._wsClient.unlistenMsg(msgName, handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*/
|
||||
sendMsg(msgName: string, msg: any): void {
|
||||
if (this._status !== WsConnectionStatus.Connected) {
|
||||
console.warn('[WebSocketClient] Cannot send message: not connected');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._wsClient && typeof this._wsClient.sendMsg === 'function') {
|
||||
this._wsClient.sendMsg(msgName, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册事件监听器
|
||||
*/
|
||||
on(event: NetEvent, callback: Function): void {
|
||||
if (!this._eventCallbacks.has(event)) {
|
||||
this._eventCallbacks.set(event, []);
|
||||
}
|
||||
this._eventCallbacks.get(event)!.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消事件监听器
|
||||
*/
|
||||
off(event: NetEvent, callback: Function): void {
|
||||
const callbacks = this._eventCallbacks.get(event);
|
||||
if (callbacks) {
|
||||
const index = callbacks.indexOf(callback);
|
||||
if (index > -1) {
|
||||
callbacks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
*/
|
||||
private emit(event: NetEvent, ...args: any[]): void {
|
||||
const callbacks = this._eventCallbacks.get(event);
|
||||
if (callbacks) {
|
||||
callbacks.forEach(callback => {
|
||||
try {
|
||||
callback(...args);
|
||||
} catch (error) {
|
||||
console.error('[WebSocketClient] Event callback error:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接状态
|
||||
*/
|
||||
get status(): WsConnectionStatus {
|
||||
return this._status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否已连接
|
||||
*/
|
||||
get isConnected(): boolean {
|
||||
return this._status === WsConnectionStatus.Connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始客户端实例
|
||||
*/
|
||||
get rawClient(): any {
|
||||
return this._wsClient;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "4a49d854-9ed8-4344-825f-9439bb287819",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user