New file |
| | |
| | | import { ref, onBeforeUnmount, onMounted } from 'vue'; |
| | | |
| | | const DEFAULT_OPTIONS = { |
| | | url: '', // websocket url |
| | | heartBeatData: '', // 你的心跳数据 |
| | | heartBeatInterval: 60 * 1000, // 心跳间隔,单位ms |
| | | reconnectInterval: 5000, // 断线重连间隔,单位ms |
| | | maxReconnectAttempts: 10 // 最大重连次数 |
| | | }; |
| | | |
| | | export const SocketStatus = { |
| | | Connecting: '正在连接...', //表示正在连接,这是初始状态。 |
| | | Connected: '连接已建立', //表示连接已经建立。 |
| | | Disconnecting: '连接正在关闭', //表示连接正在关闭。 |
| | | Disconnected: '连接已断开' //表示连接已经关闭 |
| | | }; |
| | | |
| | | const SocketCloseCode = 1000; |
| | | |
| | | export default function useWebSocket(options = {}) { |
| | | const state = { |
| | | options: { ...DEFAULT_OPTIONS, ...options }, |
| | | |
| | | socket: null, |
| | | reconnectAttempts: 0, |
| | | reconnectTimeout: null, |
| | | |
| | | heartBetaSendTimer: null, // 心跳发送定时器 |
| | | heartBetaTimeoutTimer: null // 心跳超时定时器 |
| | | }; |
| | | |
| | | // 连接状态 |
| | | const status = ref(SocketStatus.Disconnected); |
| | | const message = ref(null); |
| | | const error = ref(null); |
| | | |
| | | // 连接 |
| | | const connect = (connectCallBack) => { |
| | | disconnect(); |
| | | |
| | | status.value = SocketStatus.Connecting; |
| | | if (!window.navigator.onLine) { |
| | | setTimeout(() => { |
| | | status.value = SocketStatus.Disconnected; |
| | | }, 500); |
| | | return; |
| | | } |
| | | |
| | | state.socket = new WebSocket(state.options.url); |
| | | |
| | | state.socket.onopen = openEvent => { |
| | | console.log('socket连接:', openEvent); |
| | | state.reconnectAttempts = 0; |
| | | status.value = SocketStatus.Connected; |
| | | error.value = null; |
| | | startHeartBeat(); |
| | | if(connectCallBack) { |
| | | connectCallBack(); |
| | | } |
| | | }; |
| | | |
| | | state.socket.onmessage = msgEvent => { |
| | | console.log('socket消息:', msgEvent); |
| | | |
| | | // 收到任何数据,重新开始心跳 |
| | | startHeartBeat(); |
| | | |
| | | const { data } = msgEvent; |
| | | const msg = JSON.parse(data); |
| | | |
| | | //心跳数据, 可自行修改 |
| | | if (+msg.msg_id === 0) { |
| | | return; |
| | | } |
| | | message.value = msg; |
| | | }; |
| | | |
| | | state.socket.onclose = closeEvent => { |
| | | console.log('socket关闭:', closeEvent); |
| | | status.value = SocketStatus.Disconnected; |
| | | // 非正常关闭,尝试重连 |
| | | if (closeEvent.code !== SocketCloseCode) { |
| | | reconnect(); |
| | | } |
| | | }; |
| | | |
| | | state.socket.onerror = errEvent => { |
| | | console.log('socket报错:', errEvent); |
| | | status.value = SocketStatus.Disconnected; |
| | | error.value = errEvent; |
| | | // 连接失败,尝试重连 |
| | | reconnect(); |
| | | }; |
| | | }; |
| | | |
| | | const disconnect = () => { |
| | | if (state.socket && (state.socket.OPEN || state.socket.CONNECTING)) { |
| | | console.log('socket断开连接'); |
| | | status.value = SocketStatus.Disconnecting; |
| | | state.socket.onmessage = null; |
| | | state.socket.onerror = null; |
| | | state.socket.onclose = null; |
| | | // 发送关闭帧给服务端 |
| | | state.socket.close(SocketCloseCode, 'normal closure'); |
| | | status.value = SocketStatus.Disconnected; |
| | | state.socket = null; |
| | | } |
| | | stopHeartBeat(); |
| | | stopReconnect(); |
| | | }; |
| | | |
| | | // 发送消息 |
| | | const sendMessage = (data) => { |
| | | if (state.socket && status.value === SocketStatus.Connected) { |
| | | state.socket.send(JSON.stringify(data)); |
| | | console.log('socket发送:', JSON.stringify(data)); |
| | | } |
| | | }; |
| | | |
| | | const startHeartBeat = () => { |
| | | stopHeartBeat(); |
| | | onHeartBeat(() => { |
| | | if (status.value === SocketStatus.Connected) { |
| | | state.socket.send(state.options.heartBeatData); |
| | | console.log('socket心跳发送:', state.options.heartBeatData); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const onHeartBeat = callback => { |
| | | state.heartBetaSendTimer = setTimeout(() => { |
| | | callback && callback(); |
| | | state.heartBetaTimeoutTimer = setTimeout(() => { |
| | | // 心跳超时,直接关闭socket,抛出自定义code=4444, onclose里进行重连 |
| | | state.socket.close(4444, 'heart timeout'); |
| | | }, state.options.heartBeatInterval); |
| | | }, state.options.heartBeatInterval); |
| | | }; |
| | | |
| | | const stopHeartBeat = () => { |
| | | state.heartBetaSendTimer && clearTimeout(state.heartBetaSendTimer); |
| | | state.heartBetaTimeoutTimer && clearTimeout(state.heartBetaTimeoutTimer); |
| | | }; |
| | | |
| | | // 重连 |
| | | const reconnect = () => { |
| | | if (status.value === SocketStatus.Connected || status.value === SocketStatus.Connecting) { |
| | | return; |
| | | } |
| | | stopHeartBeat(); |
| | | if (state.reconnectAttempts < state.options.maxReconnectAttempts) { |
| | | console.log('socket重连:', state.reconnectAttempts); |
| | | |
| | | // 重连间隔,5秒起步,下次递增1秒 |
| | | const interval = Math.max(state.options.reconnectInterval, state.reconnectAttempts * 1000); |
| | | console.log('间隔时间:', interval); |
| | | state.reconnectTimeout = setTimeout(() => { |
| | | if (status.value !== SocketStatus.Connected && status.value !== SocketStatus.Connecting) { |
| | | connect(); |
| | | } |
| | | }, interval); |
| | | state.reconnectAttempts += 1; |
| | | } else { |
| | | status.value = SocketStatus.Disconnected; |
| | | stopReconnect(); |
| | | } |
| | | }; |
| | | |
| | | // 停止重连 |
| | | const stopReconnect = () => { |
| | | state.reconnectTimeout && clearTimeout(state.reconnectTimeout); |
| | | }; |
| | | |
| | | onBeforeUnmount(() => { |
| | | if (status.value === SocketStatus.Connected || status.value === SocketStatus.Connecting) { |
| | | disconnect(); |
| | | } |
| | | }); |
| | | |
| | | return { |
| | | status, |
| | | message, |
| | | error, |
| | | connect, |
| | | disconnect, |
| | | sendMessage |
| | | }; |
| | | } |