|
@@ -0,0 +1,121 @@
|
|
|
|
|
+import useStore from '@/pages/store'
|
|
|
|
|
+import { injectable, Service } from './service'
|
|
|
|
|
+
|
|
|
|
|
+@injectable
|
|
|
|
|
+export default class SocketService extends Service {
|
|
|
|
|
+ private _socket: WebSocket | null = null
|
|
|
|
|
+
|
|
|
|
|
+ private _reconnectTimeout: string | number | NodeJS.Timeout | undefined = undefined
|
|
|
|
|
+
|
|
|
|
|
+ private _ping = JSON.stringify({ event: 'heartbeat', content: 'ping' })
|
|
|
|
|
+
|
|
|
|
|
+ private _reconnectTime = 3000
|
|
|
|
|
+
|
|
|
|
|
+ private _heartInterval: string | number | NodeJS.Timeout | undefined = undefined
|
|
|
|
|
+
|
|
|
|
|
+ private _messageTime = 0
|
|
|
|
|
+
|
|
|
|
|
+ private _store = useStore()
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 连接
|
|
|
|
|
+ */
|
|
|
|
|
+ private _connect() {
|
|
|
|
|
+ const token = this._store.token
|
|
|
|
|
+ if (!token) return null
|
|
|
|
|
+ const url = `${import.meta.env.VITE_SOCKET_API}/${token}`
|
|
|
|
|
+ const socket = new WebSocket(url)
|
|
|
|
|
+ socket.onerror = this._onError.bind(this)
|
|
|
|
|
+ socket.onopen = this._onOpen.bind(this)
|
|
|
|
|
+ socket.onmessage = this._onMessage.bind(this)
|
|
|
|
|
+ socket.onclose = this._onClose.bind(this)
|
|
|
|
|
+ this._socket = socket
|
|
|
|
|
+ return socket
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 重连
|
|
|
|
|
+ */
|
|
|
|
|
+ private _reConnect() {
|
|
|
|
|
+ this._onClose()
|
|
|
|
|
+ this._reconnectTimeout = setTimeout(() => {
|
|
|
|
|
+ this._socket = this._connect()
|
|
|
|
|
+ console.log('网络连接已断开,正在尝试重新连接...')
|
|
|
|
|
+ }, this._reconnectTime)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 连接错误,重连
|
|
|
|
|
+ */
|
|
|
|
|
+ private _onError() {
|
|
|
|
|
+ this._reConnect()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 连接打开,心跳检测
|
|
|
|
|
+ */
|
|
|
|
|
+ private _onOpen() {
|
|
|
|
|
+ this._socket?.send(this._ping)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 心跳检测
|
|
|
|
|
+ */
|
|
|
|
|
+ private _heartbeat(iTime: number, pTime: number) {
|
|
|
|
|
+ if (this._heartInterval) clearInterval(this._heartInterval)
|
|
|
|
|
+ return setInterval(() => {
|
|
|
|
|
+ const t = new Date().getTime()
|
|
|
|
|
+ if ((t - this._messageTime) > pTime) {
|
|
|
|
|
+ this._reConnect()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (this._socket?.readyState === 1) this._socket?.send(this._ping)
|
|
|
|
|
+ }
|
|
|
|
|
+ }, iTime)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 接收消息
|
|
|
|
|
+ */
|
|
|
|
|
+ private _onMessage(evt: Any) {
|
|
|
|
|
+ this._messageTime = new Date().getTime()
|
|
|
|
|
+ const data = JSON.parse(evt.data)
|
|
|
|
|
+ if (data.event === 'connect') {
|
|
|
|
|
+ const iTime = data.content.ping_interval * 1000
|
|
|
|
|
+ const pTime = data.content.ping_timeout * 1000
|
|
|
|
|
+ this._heartInterval = this._heartbeat(iTime, pTime)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this._store.setSocketMSg(data)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 关闭连接
|
|
|
|
|
+ */
|
|
|
|
|
+ private _onClose() {
|
|
|
|
|
+ clearTimeout(this._reconnectTimeout)
|
|
|
|
|
+ clearInterval(this._heartInterval)
|
|
|
|
|
+ this._socket = null
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 链接 */
|
|
|
|
|
+ public connect() {
|
|
|
|
|
+ return this._connect()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 关闭 */
|
|
|
|
|
+ public close() {
|
|
|
|
|
+ if (this._socket) this._socket.close()
|
|
|
|
|
+ this._store.setSocketMSg(null)
|
|
|
|
|
+ this._onClose()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 推送消息
|
|
|
|
|
+ *
|
|
|
|
|
+ */
|
|
|
|
|
+ public emit(event: string, data: object) {
|
|
|
|
|
+ const content = JSON.stringify({ event, data })
|
|
|
|
|
+ if (!this._socket && this._socket!.readyState !== 1) return
|
|
|
|
|
+ this._socket?.send(content)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|