You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
3.0 KiB
123 lines
3.0 KiB
import { Subject } from "rxjs";
|
|
import type { Datagram } from "./wsTypes";
|
|
|
|
export type SocketState = "open" | "close" | "error";
|
|
|
|
class WebSocketClient {
|
|
private ws: WebSocket | null = null;
|
|
private url: string;
|
|
private reconnectAttempts: number = -1;
|
|
private maxReconnectAttempts: number = 5;
|
|
private reconnectInterval: number = 3000;
|
|
|
|
private dataSub = new Subject<Datagram>();
|
|
get dataOb() {
|
|
return this.dataSub.asObservable();
|
|
}
|
|
private stateSub = new Subject<SocketState>();
|
|
get stateOb() {
|
|
return this.stateSub.asObservable();
|
|
}
|
|
constructor(url: string) {
|
|
this.url = url;
|
|
}
|
|
|
|
// 连接 WebSocket
|
|
connect(): void {
|
|
try {
|
|
// WebSocket.CONNECTING (0) WebSocket.OPEN (1)
|
|
if (this.ws && this.ws.readyState <= 1) {
|
|
// 已连接
|
|
console.log(`${this.url} 正在连接或已连接,无需重复连接`);
|
|
} else {
|
|
this.ws = new WebSocket(this.url);
|
|
this.bindEvents();
|
|
}
|
|
} catch (error) {
|
|
console.error("WebSocket 连接失败:", error);
|
|
this.reconnect();
|
|
}
|
|
}
|
|
|
|
// 绑定事件
|
|
private bindEvents(): void {
|
|
if (!this.ws) return;
|
|
|
|
// 连接建立时的处理
|
|
this.ws.onopen = () => {
|
|
console.log("WebSocket 连接已建立");
|
|
this.reconnectAttempts = -1; // 重置重连次数
|
|
this.stateSub.next("open");
|
|
};
|
|
|
|
// 接收消息的处理
|
|
this.ws.onmessage = (event: MessageEvent) => {
|
|
try {
|
|
const data = JSON.parse(event.data) as Datagram;
|
|
// console.log("🚀 ~ WebSocketClient ~ bindEvents ~ data:", data);
|
|
if (data.type === "cmd") {
|
|
this.dataSub.next({ type: data.type, data: { ...data.data, success: data.data.status === "D0000" } });
|
|
} else {
|
|
this.dataSub.next(data);
|
|
}
|
|
} catch (error) {
|
|
console.error("消息解析错误:", error);
|
|
}
|
|
};
|
|
|
|
this.ws.onclose = () => {
|
|
this.stateSub.next("close");
|
|
console.log("WebSocket 连接已关闭");
|
|
this.reconnect();
|
|
};
|
|
|
|
this.ws.onerror = error => {
|
|
this.stateSub.next("error");
|
|
console.error("WebSocket 错误:", error);
|
|
};
|
|
}
|
|
|
|
// 重连机制
|
|
private reconnect(): void {
|
|
if (this.reconnectAttempts === -1) {
|
|
this.reconnectAttempts = 0;
|
|
}
|
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
console.log("达到最大重连次数,停止重连");
|
|
this.reconnectAttempts = -1;
|
|
return;
|
|
}
|
|
|
|
setTimeout(() => {
|
|
console.log(`尝试第 ${this.reconnectAttempts + 1} 次重连...`);
|
|
this.reconnectAttempts++;
|
|
this.connect();
|
|
}, this.reconnectInterval);
|
|
}
|
|
|
|
// 关闭连接
|
|
disconnect(): void {
|
|
if (this.ws) {
|
|
this.ws.close();
|
|
this.ws = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
const urlSocketMap = new Map<string, WebSocketClient>();
|
|
|
|
// 导出 WebSocket 客户端
|
|
export const createWebSocket = (url: string): WebSocketClient => {
|
|
if (urlSocketMap.has(url)) {
|
|
return urlSocketMap.get(url)!;
|
|
} else {
|
|
const client = new WebSocketClient(url);
|
|
urlSocketMap.set(url, client);
|
|
return client;
|
|
}
|
|
};
|
|
|
|
// export const sharedWsUrl = `ws://${window.location.hostname}:8080/ws`;
|
|
export const sharedWsUrl = `ws://${import.meta.env.VITE_API_HOST}:${import.meta.env.VITE_API_PORT}${
|
|
import.meta.env.VITE_WS_PATH
|
|
}`;
|