cashier_wx/common/js/websocket.js

319 lines
8.5 KiB
JavaScript

import {
ref,
onMounted,
onBeforeUnmount
} from 'vue';
const useWebSocket = (options = {}) => {
const {
heartbeatInterval = 10000, //心跳是10秒一次
reconnectInterval = 3000, //重新连接间隔时间的一个参数
maxReconnectAttempts = 3, //最大重连接次数
initialReconnectInterval = 3000, // 初始重连间隔
initMessage,
initMessageRetryCount = 3, // 新增:初始化消息发送重试次数
initMessageRetryInterval = 2000, // 新增:初始化消息重试间隔
maxReconnectDuration = Infinity
} = options;
const autoReconnect = ref(true); //是否自动重新连接
const socketTask = ref(null);
const isConnected = ref(false); //表示是否已连接上。
const heartbeatTimer = ref(null); //心跳定时器
const reconnectTimer = ref(null); //重连定时器
const reconnectAttempts = ref(0); //重连的尝试次数
const isNetworkConnected = ref(true); //监听当前网络连接状态
const isManuallyClosed = ref(false); //是否是被手动关闭的
const receivedMessages = ref([]); //储从 WebSocket 服务器接收到的消息
const initMessageSendAttempts = ref(0); //初始化连接多少次
const reconnectStartTime = ref(0); //新增:记录重连开始时间
// 关闭现有连接并清理资源
const closeExistingConnection = () => {
if (socketTask.value) {
// 关闭 WebSocket 连接
socketTask.value.close({
success: () => {
console.log('WebSocket 连接已关闭');
},
fail: (err) => {
console.error('关闭 WebSocket 连接失败:', err);
}
});
// 清除心跳定时器
clearInterval(heartbeatTimer.value);
heartbeatTimer.value = null;
// 清除重连定时器
clearTimeout(reconnectTimer.value);
reconnectTimer.value = null;
// 标记连接已断开
isConnected.value = false;
}
};
// 连接 WebSocket
const connect = () => {
if (!isNetworkConnected.value) {
uni.showToast({
title: '网络未连接...',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 1000)
console.log('网络未连接,暂不尝试连接 WebSocket');
return;
}
// 关闭现有连接并清理资源
closeExistingConnection();
socketTask.value = uni.connectSocket({
url: uni.conf.baseUrlwws,
success: (res) => {
isConnected.value = true;
reconnectAttempts.value = 0;
// 监听初始化成功在开启心跳
// startHeartbeat();
},
fail: () => {
console.error('WebSocket 连接失败,尝试重连');
if (autoReconnect.value) {
handleReconnect();
}
}
});
if (socketTask.value) {
socketTask.value.onOpen(() => {
// 初始化 初始购物车
sendMessage(initMessage)
});
socketTask.value.onMessage((res) => {
let list = JSON.parse(res.data)
console.log(list, 'receivedMessages.value')
receivedMessages.value.push(list);
if (res.msg == 'ok') {
console.log('心跳响应正常');
// 心跳正常,重置重连尝试次数
reconnectAttempts.value = 0;
clearTimeout(reconnectTimer.value);
}
});
socketTask.value.onClose((res) => {
console.log(res, 'WebSocket 连接已关闭,尝试重连');
isConnected.value = false;
clearInterval(heartbeatTimer.value); // 停止心跳定时器
clearTimeout(reconnectTimer.value); // 清除重连定时器
if (res.code == '1006') {
console.log('服务器正常关闭,停止重连');
uni.navigateBack()
autoReconnect.value = false;
return false;
}
if (autoReconnect.value && !isManuallyClosed.value) {
handleReconnect();
}
});
socketTask.value.onError((err) => {
console.error('WebSocket 连接发生错误:', err);
isConnected.value = false;
clearInterval(heartbeatTimer.value);
if (autoReconnect.value && !isManuallyClosed.value) {
handleReconnect();
}
});
} else {
console.error('socketTask 未正确初始化');
}
};
// 启动心跳机制
const startHeartbeat = () => {
if (!isNetworkConnected.value) {
console.log('网络未连接,暂停心跳');
uni.showToast({
title: '网络未连接...',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 1000)
return;
}
heartbeatTimer.value = setInterval(() => {
if (isConnected.value) {
socketTask.value.send({
data: JSON.stringify({
type: 'ping_interval'
}),
success: () => {
console.log('心跳消息发送成功');
const pongTimer = setTimeout(() => {
console.error('心跳超时,未收到响应,尝试重连');
clearInterval(heartbeatTimer.value);
if (autoReconnect) {
handleReconnect();
}
}, heartbeatInterval * 1.2);
socketTask.value.onMessage((res) => {
if (res.msg == 'ok') {
clearTimeout(pongTimer);
}
});
},
fail: () => {
console.error('心跳消息发送失败,尝试重连');
clearInterval(heartbeatTimer.value);
if (autoReconnect) {
handleReconnect();
}
}
});
}
}, heartbeatInterval);
};
// 手动关闭连接
const closeSocket = () => {
isManuallyClosed.value = true;
uni.navigateBack()
closeExistingConnection();
};
// 发送消息
const sendMessage = async (data) => {
if (isConnected.value) {
await socketTask.value.send({
data: JSON.stringify(data),
success: () => {
// console.log('消息发送成功');
},
fail: () => {
// console.error('消息发送失败');
}
});
} else {
console.error('WebSocket 未连接,无法发送消息');
}
};
// 处理重连逻辑
const handleReconnect = () => {
if (!isNetworkConnected.value) {
console.log('网络未连接,暂停重连');
return;
}
if (isManuallyClosed.value) {
console.log('手动关闭连接,不进行重连');
return;
}
if (reconnectAttempts.value < maxReconnectAttempts) {
reconnectAttempts.value++;
const reconnectInterval = initialReconnectInterval * Math.pow(2, reconnectAttempts.value - 1);
const randomizedInterval = reconnectInterval + Math.floor(Math.random() * 1000);
console.log(`尝试第 ${reconnectAttempts.value} 次重连,重连间隔: ${randomizedInterval}ms...`);
reconnectTimer.value = setTimeout(() => {
connect();
}, randomizedInterval);
} else {
console.error('重连次数达到上限,停止重连');
uni.showToast({
title: '重连次数达到上限,停止重连',
icon: 'none'
});
uni.navigateBack()
}
};
// 发送初始化消息
const sendInitMessage = async () => {
if (initMessageSendAttempts.value < initMessageRetryCount) {
initMessageSendAttempts.value++;
await socketTask.value.send({
data: JSON.stringify(initMessage),
success: () => {
console.log('初始化消息发送成功');
initMessageSendAttempts.value = 0; // 重置尝试次数
},
fail: () => {
console.log(
`初始化消息发送失败,第 ${initMessageSendAttempts.value} 次尝试,将在 ${initMessageRetryInterval} 后重试`
);
setTimeout(() => {
sendInitMessage();
}, initMessageRetryInterval);
}
});
} else {
console.error('初始化消息发送失败,已达到最大重试次数');
// initMessageSendAttempts.value = 0; // 重置尝试次数
}
};
// 网络状态监听
const initNetworkListener = () => {
uni.getSystemInfo({
success: (res) => {
if (res.platform !== 'devtools') {
uni.onNetworkStatusChange((statusRes) => {
isNetworkConnected.value = statusRes.isConnected;
if (statusRes.isConnected && !isManuallyClosed.value) {
console.log('网络已连接,尝试重新连接 WebSocket');
if (!isConnected.value && autoReconnect.value) {
connect();
}
} else if (!statusRes.isConnected) {
console.log('网络已断开,暂停 WebSocket 操作');
clearInterval(heartbeatTimer.value);
clearTimeout(reconnectTimer.value);
if (socketTask.value) {
socketTask.value.close();
isConnected.value = false;
}
}
});
}
},
fail: (err) => {
console.error('获取系统信息失败:', err);
}
});
uni.getNetworkType({
success: (res) => {
isNetworkConnected.value = res.networkType !== 'none';
if (!isNetworkConnected.value) {
console.log('初始网络未连接,暂不尝试连接 WebSocket');
}
}
});
};
onMounted(() => {
initNetworkListener();
connect();
});
onBeforeUnmount(() => {
closeSocket();
});
return {
isConnected,
sendMessage,
closeSocket,
receivedMessages
};
};
export default useWebSocket;