2022-12-08 21:14:02 +08:00
|
|
|
|
import Singleton from "../Base/Singleton";
|
|
|
|
|
import { ApiMsgEnum, IModel } from "../Common";
|
|
|
|
|
import { binaryEncode, binaryDecode } from "../Common/Binary";
|
2022-12-01 22:26:41 +08:00
|
|
|
|
|
2022-12-08 21:14:02 +08:00
|
|
|
|
const TIMEOUT = 5000;
|
2022-12-01 22:26:41 +08:00
|
|
|
|
|
2022-12-07 22:24:46 +08:00
|
|
|
|
interface IItem {
|
|
|
|
|
cb: Function;
|
|
|
|
|
ctx: unknown;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-03 20:06:57 +08:00
|
|
|
|
export interface ICallApiRet<T> {
|
2022-12-01 22:26:41 +08:00
|
|
|
|
success: boolean;
|
|
|
|
|
error?: Error;
|
2022-12-08 21:14:02 +08:00
|
|
|
|
res?: T;
|
2022-12-01 22:26:41 +08:00
|
|
|
|
}
|
|
|
|
|
export default class NetworkManager extends Singleton {
|
|
|
|
|
static get Instance() {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
return super.GetInstance<NetworkManager>();
|
2022-12-01 22:26:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-08 21:14:02 +08:00
|
|
|
|
ws: WebSocket;
|
|
|
|
|
port = 8888;
|
|
|
|
|
maps: Map<ApiMsgEnum, Array<IItem>> = new Map();
|
|
|
|
|
isConnected = false;
|
2022-12-01 22:26:41 +08:00
|
|
|
|
|
|
|
|
|
connect() {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
2022-12-03 21:28:38 +08:00
|
|
|
|
if (this.isConnected) {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
resolve(true);
|
|
|
|
|
return;
|
2022-12-03 21:28:38 +08:00
|
|
|
|
}
|
2022-12-08 21:14:02 +08:00
|
|
|
|
this.ws = new WebSocket(`ws://localhost:${this.port}`);
|
2022-12-07 22:24:46 +08:00
|
|
|
|
//onmessage接受的数据类型,只有在后端返回字节数组的时候才有效果
|
2022-12-08 21:14:02 +08:00
|
|
|
|
this.ws.binaryType = "arraybuffer";
|
2022-12-07 22:24:46 +08:00
|
|
|
|
|
2022-12-01 22:26:41 +08:00
|
|
|
|
this.ws.onopen = () => {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
this.isConnected = true;
|
|
|
|
|
resolve(true);
|
|
|
|
|
};
|
2022-12-01 22:26:41 +08:00
|
|
|
|
|
2022-12-07 22:24:46 +08:00
|
|
|
|
this.ws.onerror = (e) => {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
this.isConnected = false;
|
|
|
|
|
console.log(e);
|
|
|
|
|
reject("ws onerror");
|
|
|
|
|
};
|
2022-12-01 22:26:41 +08:00
|
|
|
|
|
|
|
|
|
this.ws.onclose = () => {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
this.isConnected = false;
|
|
|
|
|
reject("ws onclose");
|
|
|
|
|
};
|
2022-12-01 22:26:41 +08:00
|
|
|
|
|
|
|
|
|
this.ws.onmessage = (e) => {
|
|
|
|
|
try {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
const json = binaryDecode(e.data);
|
|
|
|
|
const { name, data } = json;
|
2022-12-01 22:26:41 +08:00
|
|
|
|
try {
|
2022-12-04 22:10:30 +08:00
|
|
|
|
if (this.maps.has(name) && this.maps.get(name).length) {
|
2022-12-01 22:26:41 +08:00
|
|
|
|
console.log(json);
|
2022-12-08 21:14:02 +08:00
|
|
|
|
this.maps.get(name).forEach(({ cb, ctx }) => cb.call(ctx, data));
|
2022-12-01 22:26:41 +08:00
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
console.log("onmessage:", error);
|
2022-12-01 22:26:41 +08:00
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
console.log("解析失败,不是合法的JSON格式", error);
|
2022-12-01 22:26:41 +08:00
|
|
|
|
}
|
2022-12-08 21:14:02 +08:00
|
|
|
|
};
|
|
|
|
|
});
|
2022-12-01 22:26:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-08 21:14:02 +08:00
|
|
|
|
callApi<T extends keyof IModel["api"]>(name: T, data: IModel["api"][T]["req"]): Promise<ICallApiRet<IModel["api"][T]["res"]>> {
|
2022-12-03 20:06:57 +08:00
|
|
|
|
return new Promise((resolve) => {
|
2022-12-01 22:26:41 +08:00
|
|
|
|
try {
|
|
|
|
|
// 超时处理
|
|
|
|
|
const timer = setTimeout(() => {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
resolve({ success: false, error: new Error("timeout") });
|
|
|
|
|
this.unlistenMsg(name as any, cb, null);
|
|
|
|
|
}, TIMEOUT);
|
2022-12-01 22:26:41 +08:00
|
|
|
|
|
|
|
|
|
// 回调处理
|
2022-12-03 20:06:57 +08:00
|
|
|
|
const cb = (res) => {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
resolve(res);
|
|
|
|
|
clearTimeout(timer);
|
|
|
|
|
this.unlistenMsg(name as any, cb, null);
|
|
|
|
|
};
|
|
|
|
|
this.listenMsg(name as any, cb, null);
|
2022-12-01 22:26:41 +08:00
|
|
|
|
|
2022-12-08 21:14:02 +08:00
|
|
|
|
this.sendMsg(name as any, data);
|
2022-12-01 22:26:41 +08:00
|
|
|
|
} catch (error) {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
resolve({ success: false, error: error as Error });
|
2022-12-01 22:26:41 +08:00
|
|
|
|
}
|
2022-12-08 21:14:02 +08:00
|
|
|
|
});
|
2022-12-01 22:26:41 +08:00
|
|
|
|
}
|
2022-12-03 20:06:57 +08:00
|
|
|
|
|
2022-12-08 21:14:02 +08:00
|
|
|
|
async sendMsg<T extends keyof IModel["msg"]>(name: T, data: IModel["msg"][T]) {
|
|
|
|
|
const view = binaryEncode(name, data);
|
|
|
|
|
let delay = parseInt(new URLSearchParams(location.search).get("delay") || "0") || 0;
|
|
|
|
|
await new Promise((r) => setTimeout(r, delay));
|
|
|
|
|
this.ws.send(view.buffer);
|
2022-12-03 20:06:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-08 21:14:02 +08:00
|
|
|
|
listenMsg<T extends keyof IModel["msg"]>(name: T, cb: (args: IModel["msg"][T]) => void, ctx: unknown) {
|
2022-12-04 22:10:30 +08:00
|
|
|
|
if (this.maps.has(name)) {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
this.maps.get(name).push({ ctx, cb });
|
2022-12-03 20:06:57 +08:00
|
|
|
|
} else {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
this.maps.set(name, [{ ctx, cb }]);
|
2022-12-03 20:06:57 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-08 21:14:02 +08:00
|
|
|
|
unlistenMsg<T extends keyof IModel["msg"]>(name: T, cb: (args: IModel["msg"][T]) => void, ctx: unknown) {
|
2022-12-04 22:10:30 +08:00
|
|
|
|
if (this.maps.has(name)) {
|
2022-12-08 21:14:02 +08:00
|
|
|
|
const items = this.maps.get(name);
|
|
|
|
|
const index = items.findIndex((i) => cb === i.cb && i.ctx === ctx);
|
|
|
|
|
index > -1 && items.splice(index, 1);
|
2022-12-03 20:06:57 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-01 22:26:41 +08:00
|
|
|
|
}
|