428 lines
12 KiB
JavaScript
428 lines
12 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 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; |