You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
4.6 KiB
TypeScript

import { ref, onMounted, onUnmounted } from 'vue';
let heartbeatTimer: NodeJS.Timeout;
export default function () {
const socket = ref<WebSocket | null>(null);
const message = ref<string>();
const status = ref<'connecting' | 'open' | 'error' | 'closed'>('closed');
const pendingMessages = ref([]);
const reconnectAttempts = ref(0);
const maxReconnectAttempts = 5;
const isConnected = computed(() => { return status.value === 'open' });
const isConnecting = computed(() => { return status.value === 'connecting' });
const config = useRuntimeConfig();
const connectionTimeout = config.public.apiTimeout || 5000; // 5秒连接超时
// 初始化连接
const connect = () => {
if (isConnected.value || isConnecting.value) return;
status.value = 'connecting';
socket.value = new WebSocket(config.public.wsUrl, "json");
socket.value.onopen = () => {
status.value = 'open';
reconnectAttempts.value = 0;
console.log('WebSocket连接成功');
retryPendingMessages();
};
socket.value.onmessage = (event) => {
message.value = event.data;
// console.log('收到消息:', event.data);
// try {
// const json = JSON.parse(event.data);
// // console.info(json);
// const evtType = json.type;
// console.log(evtType)
// }
// finally{
// }
};
socket.value.onerror = (error) => {
status.value = 'error';
console.error('WebSocket发生错误:', error);
// 重试逻辑
if (reconnectAttempts.value < maxReconnectAttempts) {
setTimeout(connect, 1000 * reconnectAttempts.value);
reconnectAttempts.value++;
}
};
socket.value.onclose = (event) => {
status.value = 'closed';
console.log('WebSocket连接关闭');
if (!event.wasClean) {
// 非正常关闭时尝试重连
connect();
}
};
}
// 等待连接就绪
const waitForConnection = () => {
return new Promise((resolve, reject) => {
if (isConnected.value) {
resolve(true);
return;
}
// 设置连接超时
const timeout = setTimeout(() => {
reject(new Error('连接超时'));
}, connectionTimeout);
// 监听连接状态变化
const watchHandle = watch(isConnected, (newVal) => {
if (newVal) {
clearTimeout(timeout);
watchHandle(); // 取消监听
resolve(true);
}
}, { immediate: true });
});
};
// 发送消息
const send = async (name: string, json: object, token: string = '') => {
const msg = {
type: name,
drawerToken: token || localStorage.getItem('drawerToken') || '',
data: json
}
try {
// 等待连接就绪
await waitForConnection();
socket.value.send(JSON.stringify(msg));
if (name != 'ping' && name != 'MouseMove') {
console.log('发送消息:', msg);
}
} catch (error) {
console.error('消息发送失败:', error);
// 添加到待发队列
pendingMessages.value.push(JSON.stringify({ type: name, data: json }));
throw error;
}
}
// 重发待处理消息
const retryPendingMessages = () => {
while (pendingMessages.value.length > 0) {
const message = pendingMessages.value.shift();
let jsonData = JSON.parse(message);
send(jsonData.type, jsonData.data);
}
};
const startHeartbeat = () => {
heartbeatTimer = setInterval(() => {
if (socket.value?.readyState === WebSocket.OPEN) {
send("ping", {});
// console.log('发送心跳');
}
}, 5000)
}
// const reconnect = ()=>{
// setTimeout(()=>{
// console.log('尝试重新连接WebSocket');
// connect();
// },50000)
// }
onMounted(() => {
connect();
startHeartbeat();
});
onUnmounted(() => {
if (heartbeatTimer) {
clearInterval(heartbeatTimer);
};
socket.value?.close();
});
return { message, status, send, connect, waitForConnection }
}