-
Notifications
You must be signed in to change notification settings - Fork 1
/
userWebSocket.js
170 lines (145 loc) · 5.33 KB
/
userWebSocket.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import { ref } 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 = () => {
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()
}
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 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)
}
return {
status,
message,
error,
connect,
disconnect
}
}