Files
cashier_wx/common/js/websocket.js

428 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 isPongReceived = ref(false)
const allowReconnect = ref(true); // 新增:控制是否允许重连
// 关闭现有连接并清理资源
const closeExistingConnection = () => {
// 清除心跳定时器
clearInterval(heartbeatTimer.value);
heartbeatTimer.value = null;
// 清除重连定时器
clearTimeout(reconnectTimer.value);
reconnectTimer.value = null;
// 标记连接已断开
isConnected.value = false;
uni.closeSocket()
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.pro.switchTab('index/index')
}, 1000)
console.log('网络未连接,暂不尝试连接 WebSocket');
return;
}
// 关闭现有连接并清理资源
closeExistingConnection();
uni.connectSocket({
url: uni.conf.baseUrlwws,
success: (res) => {
isConnected.value = true;
// 监听初始化成功在开启心跳
startHeartbeat();
},
fail: () => {
console.error('WebSocket 连接失败,尝试重连');
if (autoReconnect.value && allowReconnect.value) {
handleReconnect();
}
}
});
uni.onSocketOpen(function(res) {
console.log('WebSocket连接已打开');
sendMessage(initMessage)
});
function sendSocketMessage(msg) {
if (isConnected.value) {
uni.sendSocketMessage({
data: msg
});
} else {
socketMsgQueue.push(msg);
}
}
uni.onSocketError(function(res) {
console.log('WebSocket连接打开失败请检查');
console.error('WebSocket 连接发生错误:', err);
isConnected.value = false;
clearInterval(heartbeatTimer.value);
if (autoReconnect.value && !isManuallyClosed.value) {
handleReconnect();
}
});
uni.onSocketMessage(function(res) {
console.log('收到服务器内容:' + res.data);
receivedMessages.value = JSON.parse(res.data)
// receivedMessages.value.push(list);
if (receivedMessages.value == 'ok' || receivedMessages.value.operate_type == 'init') {
console.log('初始化正常,心跳响应正常');
// 清除重连定时器
clearTimeout(reconnectTimer.value);
allowReconnect.value = false
reconnectTimer.value = null;
}
});
uni.onSocketClose(function(res) {
console.log('WebSocket 已关闭!');
console.log(res, 'WebSocket 连接已关闭,尝试重连');
isConnected.value = false;
clearInterval(heartbeatTimer.value); // 停止心跳定时器
clearTimeout(reconnectTimer.value); // 清除重连定时器
if (res.code == '1006' && !allowReconnect.value) {
uni.showToast({
title: '网络异常,请重新扫码',
icon: 'none'
});
autoReconnect.value = false;
setTimeout(() => {
uni.pro.switchTab('index/index');
}, 1000)
return false;
}
if (autoReconnect.value && !isManuallyClosed.value) {
handleReconnect();
}
});
if (socketTask.value) {
socketTask.value.onOpen(() => {
// 初始化 初始购物车
sendMessage(initMessage)
});
socketTask.value.onMessage((res) => {
receivedMessages.value = JSON.parse(res.data)
// receivedMessages.value.push(list);
if (receivedMessages.value == 'ok' || receivedMessages.value.operate_type == 'init') {
console.log('初始化正常,心跳响应正常');
// 清除重连定时器
clearTimeout(reconnectTimer.value);
allowReconnect.value = false
reconnectTimer.value = null;
}
});
socketTask.value.onClose((res) => {
console.log(res, 'WebSocket 连接已关闭,尝试重连');
isConnected.value = false;
clearInterval(heartbeatTimer.value); // 停止心跳定时器
clearTimeout(reconnectTimer.value); // 清除重连定时器
if (res.code == '1006' && !allowReconnect.value) {
uni.showToast({
title: '网络异常,请重新扫码',
icon: 'none'
});
autoReconnect.value = false;
setTimeout(() => {
uni.pro.switchTab('index/index');
}, 1000)
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.pro.switchTab('index/index');
}, 1000);
return;
}
heartbeatTimer.value = setInterval(() => {
if (isConnected.value) {
console.log('发送心跳消息');
isPongReceived.value = false; // 每次发送心跳消息前重置标记
socketTask.value.send({
data: JSON.stringify({
type: 'ping_interval',
set: 'shopping'
}),
success: () => {
console.log('心跳消息发送成功');
const pongTimer = setTimeout(() => {
if (!isPongReceived.value) {
console.error('心跳超时,未收到响应,尝试重连');
clearInterval(heartbeatTimer.value);
if (autoReconnect.value && reconnectAttempts.value <
maxReconnectAttempts && allowReconnect.value) {
handleReconnect();
} else {
console.error('重连次数达到上限,停止重连和心跳');
clearInterval(heartbeatTimer.value);
autoReconnect.value = false;
uni.pro.switchTab('index/index');
}
}
}, heartbeatInterval * 1.2);
const handlePong = (res) => {
try {
let data = JSON.parse(res.data);
if (data.operate_type == "init" || (data.msg === 'ok' &&
data.msg_id ==
'ping_interval')) {
isPongReceived.value = true;
console.log('收到心跳响应,清除超时定时器');
clearTimeout(pongTimer);
}
} catch (error) {
console.error('解析心跳响应数据时出错:', error);
}
};
socketTask.value.onMessage(handlePong);
},
fail: () => {
console.error('心跳消息发送失败,尝试重连');
clearInterval(heartbeatTimer.value);
if (autoReconnect.value && reconnectAttempts.value <
maxReconnectAttempts && allowReconnect.value) {
handleReconnect();
} else {
console.error('重连次数达到上限,停止重连和心跳');
clearInterval(heartbeatTimer.value);
autoReconnect.value = false;
uni.pro.switchTab('index/index');
}
}
});
}
}, heartbeatInterval);
};
// 手动关闭连接
const closeSocket = () => {
isManuallyClosed.value = true;
closeExistingConnection();
};
// 发送消息
const sendMessage = async (data) => {
if (isConnected.value) {
uni.sendSocketMessage({
data:JSON.stringify(data),
success: () => {
// console.log('消息发送成功');
},
fail: () => {
// console.error('消息发送失败');
}
});
// 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 (!allowReconnect.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);
uni.showLoading({
title: `正在努力连接..`,
mask: true
})
console.log(`尝试第 ${reconnectAttempts.value} 次重连,重连间隔: ${randomizedInterval}ms...`);
reconnectTimer.value = setTimeout(() => {
connect();
}, randomizedInterval);
} else {
console.error('重连次数达到上限,停止重连');
uni.showToast({
title: '重连次数达到上限,停止重连',
icon: 'none'
});
clearInterval(heartbeatTimer.value);
autoReconnect.value = false;
setTimeout(() => {
uni.hideLoading();
uni.pro.switchTab('index/index');
}, 1000)
}
};
// / 网络状态监听
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');
}
}
});
};
// 页面显示,尝试连接 WebSocket
const onShowconnect = () => {
if (autoReconnect.value) {
uni.showLoading({
title: `尝试再次连接`,
mask: true
})
connect();
}
}
onBeforeUnmount(() => {
closeSocket();
});
return {
isConnected,
sendMessage,
closeSocket,
receivedMessages,
closeExistingConnection,
onShowconnect,
initNetworkListener
};
};
export default useWebSocket;