class WebSocketManager {
constructor(options = {}) {
this.options = {
url: options.url || "",
reconnectInterval: options.reconnectInterval || 5000,
heartbeatInterval: options.heartbeatInterval || 30000,
maxReconnectAttempts: options.maxReconnectAttempts || 5,
heartbeatMessage:
options.heartbeatMessage || JSON.stringify({ type: "ping" }),
onOpen: options.onOpen || (() => {}),
onClose: options.onClose || (() => {}),
onError: options.onError || (() => {}),
onMessage: options.onMessage || (() => {}),
onReconnect: options.onReconnect || (() => {}),
...options,
};
this.ws = null;
this.reconnectAttempts = 0;
this.isClosing = false;
this.isManuallyClosed = false;
this.heartbeatTimer = null;
this.reconnectTimer = null;
this.id = options.id || this.generateId();
}
generateId() {
return `ws_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
}
connect() {
if (this.isClosing || this.isManuallyClosed) return;
try {
this.ws = new WebSocket(this.options.url);
this.ws.onopen = (event) => {
console.log(`WebSocket连接已建立: ${this.id}`);
this.reconnectAttempts = 0;
this.startHeartbeat();
this.options.onOpen(event, this.id);
};
this.ws.onmessage = (event) => {
if (event.data === this.options.heartbeatMessage) return;
this.options.onMessage(event, this.id);
};
this.ws.onclose = (event) => {
console.log(`WebSocket连接已关闭: ${this.id}`);
this.stopHeartbeat();
this.handleReconnect(event);
this.options.onClose(event, this.id);
};
this.ws.onerror = (event) => {
console.error(`WebSocket发生错误: ${this.id}`, event);
this.stopHeartbeat();
this.options.onError(event, this.id);
};
} catch (error) {
console.error(`WebSocket连接失败: ${this.id}`, error);
this.handleReconnect();
}
}
startHeartbeat() {
this.stopHeartbeat();
this.heartbeatTimer = setInterval(() => {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(this.options.heartbeatMessage);
}
}, this.options.heartbeatInterval);
}
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
}
handleReconnect(event) {
if (this.isClosing || this.isManuallyClosed) return;
if (this.reconnectAttempts < this.options.maxReconnectAttempts) {
this.reconnectAttempts++;
console.log(
`WebSocket尝试重连 (${this.reconnectAttempts}/${this.options.maxReconnectAttempts}): ${this.id}`
);
this.options.onReconnect(
this.reconnectAttempts,
this.options.maxReconnectAttempts,
this.id
);
this.reconnectTimer = setTimeout(() => {
this.connect();
}, this.options.reconnectInterval);
} else {
console.warn(`WebSocket重连次数已达上限: ${this.id}`);
}
}
send(data) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(data);
return true;
} else {
console.warn(`WebSocket未连接,无法发送消息: ${this.id}`);
return false;
}
}
close(code = 1000, reason = "") {
this.isManuallyClosed = true;
this.isClosing = true;
this.stopHeartbeat();
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
if (this.ws) {
this.ws.close(code, reason);
this.ws = null;
}
console.log(`WebSocket连接已手动关闭: ${this.id}`);
}
getReadyState() {
if (!this.ws) return WebSocket.CLOSED;
return this.ws.readyState;
}
getReadyStateText() {
const states = {
[WebSocket.CONNECTING]: "CONNECTING",
[WebSocket.OPEN]: "OPEN",
[WebSocket.CLOSING]: "CLOSING",
[WebSocket.CLOSED]: "CLOSED",
};
return states[this.getReadyState()];
}
}
class WebSocketPool {
constructor() {
this.connections = new Map();
}
addConnection(options) {
const wsManager = new WebSocketManager(options);
this.connections.set(wsManager.id, wsManager);
wsManager.connect();
return wsManager.id;
}
getConnection(id) {
return this.connections.get(id);
}
sendTo(id, data) {
const connection = this.getConnection(id);
if (connection) {
return connection.send(data);
}
console.warn(`WebSocket连接不存在: ${id}`);
return false;
}
closeConnection(id, code, reason) {
const connection = this.connections.get(id);
if (connection) {
connection.close(code, reason);
this.connections.delete(id);
console.log(`连接 ${id} 已从连接池中移除`);
}
}
closeAllConnections(code = 1000, reason = "") {
for (const [id, connection] of this.connections) {
connection.close(code, reason);
}
this.connections.clear();
console.log("所有WebSocket连接已关闭");
}
getAllConnectionsInfo() {
const info = [];
for (const [id, connection] of this.connections) {
info.push({
id,
url: connection.options.url,
readyState: connection.getReadyStateText(),
});
}
return info;
}
getConnectionCount() {
return this.connections.size;
}
removeConnection(id) {
const connection = this.connections.get(id);
if (connection) {
connection.close();
this.connections.delete(id);
}
}
}
const wsPool = new WebSocketPool();
export { WebSocketManager, WebSocketPool, wsPool };
export const createWebSocket = (options) => {
return wsPool.addConnection(options);
};
export const closeWebSocket = (id, code, reason) => {
wsPool.closeConnection(id, code, reason);
};
export const closeAllWebSockets = (code, reason) => {
wsPool.closeAllConnections(code, reason);
};
export const sendWebSocketMessage = (id, data) => {
return wsPool.sendTo(id, data);
};