|
@@ -1,184 +1,225 @@
|
|
|
// src/stores/websocket.ts
|
|
|
import { defineStore } from 'pinia'
|
|
|
-import { ref } from 'vue'
|
|
|
import { $root as protobuf } from '@/common/proto/proto'
|
|
|
import * as Constant from '@/common/constant/Constant'
|
|
|
-import { useWalletStore } from "@/stores/modules/walletStore";
|
|
|
-
|
|
|
-// MessageType = $root.lookupType("protocol.Message");
|
|
|
-
|
|
|
-
|
|
|
+import { useWalletStore } from "@/stores/modules/walletStore"
|
|
|
+
|
|
|
+export const useWebSocketStore = defineStore('webSocketStore', {
|
|
|
+ // 状态定义
|
|
|
+ state: () => ({
|
|
|
+ socket: null ,
|
|
|
+ peer: null,
|
|
|
+ lockConnection: false,
|
|
|
+ reconnectAttempts: 0,
|
|
|
+ maxReconnectAttempts: 5,
|
|
|
+ reconnectInterval: 3000,
|
|
|
+ messages: [] ,
|
|
|
+ unreadMessages: [] ,
|
|
|
+ lastMessage: null ,
|
|
|
+
|
|
|
+ // 心跳检测配置
|
|
|
+ heartCheck: {
|
|
|
+ timeout: 10000,
|
|
|
+ timeoutObj: null ,
|
|
|
+ serverTimeoutObj: null,
|
|
|
+ num: 3
|
|
|
+ }
|
|
|
+ }),
|
|
|
|
|
|
-export const useWebSocketStore = defineStore('webSocketStore', () => {
|
|
|
- const socket = ref(null) // 创建WebSocket对象
|
|
|
- const peer = ref(null) // 创建RTCPeerConnection对象
|
|
|
- const lockConnection = ref(false)
|
|
|
- const reconnectAttempts = ref(0)
|
|
|
- const maxReconnectAttempts = 5
|
|
|
- const reconnectInterval = 3000
|
|
|
+ // 计算属性
|
|
|
+ getters: {
|
|
|
+ isConnected: (state) => state.socket?.readyState === WebSocket.OPEN,
|
|
|
+ hasUnreadMessages: (state) => state.unreadMessages.length > 0
|
|
|
+ },
|
|
|
|
|
|
- // 心跳检测
|
|
|
- const heartCheck = {
|
|
|
- timeout: 10000,
|
|
|
- timeoutObj: null,
|
|
|
- serverTimeoutObj: null,
|
|
|
- num: 3,
|
|
|
- start: function () {
|
|
|
+ // 方法
|
|
|
+ actions: {
|
|
|
+ startHeartbeat() {
|
|
|
const self = this
|
|
|
- const _num = this.num
|
|
|
+ const _num = this.heartCheck.num
|
|
|
|
|
|
- this.timeoutObj && clearTimeout(this.timeoutObj)
|
|
|
- this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj)
|
|
|
+ this.heartCheck.timeoutObj && clearTimeout(this.heartCheck.timeoutObj)
|
|
|
+ this.heartCheck.serverTimeoutObj && clearTimeout(this.heartCheck.serverTimeoutObj)
|
|
|
|
|
|
- this.timeoutObj = setTimeout(() => {
|
|
|
- if (socket.value?.readyState === 1) {
|
|
|
+ this.heartCheck.timeoutObj = setTimeout(() => {
|
|
|
+ if (this.socket?.readyState === WebSocket.OPEN) {
|
|
|
const data = {
|
|
|
type: "heatbeat",
|
|
|
content: "ping",
|
|
|
}
|
|
|
- const message = protobuf.lookup("protocol.Message")
|
|
|
- const messagePB = message.create(data)
|
|
|
- socket.value.send(message.encode(messagePB).finish())
|
|
|
+ const MessageType = protobuf.lookupType("protocol.Message")
|
|
|
+ const messagePB = MessageType.create(data)
|
|
|
+ const buffer = MessageType.encode(messagePB).finish()
|
|
|
+ this.socket.send(buffer)
|
|
|
}
|
|
|
|
|
|
- self.serverTimeoutObj = setTimeout(() => {
|
|
|
+ self.heartCheck.serverTimeoutObj = setTimeout(() => {
|
|
|
_num--
|
|
|
if (_num <= 0) {
|
|
|
console.log("the ping num is more then 3, close socket!")
|
|
|
- socket.value?.close()
|
|
|
+ this.socket?.close()
|
|
|
}
|
|
|
- }, self.timeout)
|
|
|
- }, this.timeout)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 连接WebSocket
|
|
|
- const connect = (userUuid) => {
|
|
|
- console.log("开始连接...")
|
|
|
- peer.value = new RTCPeerConnection()
|
|
|
- socket.value = new WebSocket(`ws://192.168.0.59:8888/api/v1/socket.io?user=${userUuid}`)
|
|
|
+ }, self.heartCheck.timeout)
|
|
|
+ }, this.heartCheck.timeout)
|
|
|
+ },
|
|
|
+
|
|
|
+ resetHeartbeat() {
|
|
|
+ this.heartCheck.timeoutObj && clearTimeout(this.heartCheck.timeoutObj)
|
|
|
+ this.heartCheck.serverTimeoutObj && clearTimeout(this.heartCheck.serverTimeoutObj)
|
|
|
+ this.heartCheck.num = 3
|
|
|
+ },
|
|
|
+
|
|
|
+ connect(userUuid) {
|
|
|
+ console.log("开始连接...")
|
|
|
+ this.disconnect() // 确保先断开现有连接
|
|
|
+
|
|
|
+ this.peer = new RTCPeerConnection()
|
|
|
+ this.socket = new WebSocket(`ws://192.168.0.59:8888/api/v1/socket.io?user=${userUuid}`)
|
|
|
|
|
|
- socket.value.onopen = () => {
|
|
|
- heartCheck.start()
|
|
|
- console.log("链接")
|
|
|
- webrtcConnection()
|
|
|
- }
|
|
|
+ this.socket.onopen = () => {
|
|
|
+ this.startHeartbeat()
|
|
|
+ console.log("WebSocket连接成功")
|
|
|
+ this.webrtcConnection()
|
|
|
+ }
|
|
|
|
|
|
- socket.value.onmessage = (event) => {
|
|
|
- heartCheck.start()
|
|
|
- handleMessage(event.data)
|
|
|
- }
|
|
|
+ this.socket.onmessage = (event) => {
|
|
|
+ this.startHeartbeat()
|
|
|
+ this.handleMessage(event.data)
|
|
|
+ }
|
|
|
|
|
|
- socket.value.onclose = () => {
|
|
|
- console.log("关闭并重新连接-->--->")
|
|
|
- reconnect()
|
|
|
- }
|
|
|
+ this.socket.onclose = () => {
|
|
|
+ console.log("连接关闭,尝试重新连接...")
|
|
|
+ this.resetHeartbeat()
|
|
|
+ this.reconnect()
|
|
|
+ }
|
|
|
|
|
|
- socket.value.onerror = () => {
|
|
|
- console.log("error----->>>>")
|
|
|
- reconnect()
|
|
|
- }
|
|
|
- }
|
|
|
+ this.socket.onerror = (error) => {
|
|
|
+ console.error("WebSocket错误:", error)
|
|
|
+ this.resetHeartbeat()
|
|
|
+ this.reconnect()
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- // 处理接收到的消息
|
|
|
- const handleMessage = (data) => {
|
|
|
- const messageProto = protobuf.lookupType("protocol.Message")
|
|
|
- const reader = new FileReader()
|
|
|
-
|
|
|
- reader.readAsArrayBuffer(data)
|
|
|
- reader.onload = (event) => {
|
|
|
- const messagePB = messageProto.decode(new Uint8Array(event.target?.result))
|
|
|
- // 单独解码
|
|
|
- console.log(messagePB)
|
|
|
+ handleMessage(data) {
|
|
|
+ const MessageType = protobuf.lookupType("protocol.Message")
|
|
|
+ const reader = new FileReader()
|
|
|
|
|
|
- if (messagePB.type === "heatbeat") return
|
|
|
-
|
|
|
- if (messagePB.type === Constant.MESSAGE_TRANS_TYPE) {
|
|
|
- dealWebRtcMessage(messagePB)
|
|
|
- return
|
|
|
+ reader.onload = (event) => {
|
|
|
+ try {
|
|
|
+ const messagePB = MessageType.decode(new Uint8Array(event.target?.result))
|
|
|
+ const message = MessageType.toObject(messagePB, {
|
|
|
+ longs: String,
|
|
|
+ enums: String,
|
|
|
+ bytes: String,
|
|
|
+ })
|
|
|
+
|
|
|
+ console.log("收到消息:", message)
|
|
|
+
|
|
|
+ // 更新状态
|
|
|
+ this.messages.push(message)
|
|
|
+ this.lastMessage = message
|
|
|
+
|
|
|
+ if (message.type === "heatbeat") return
|
|
|
+
|
|
|
+ if (message.type === Constant.MESSAGE_TRANS_TYPE) {
|
|
|
+ this.dealWebRtcMessage(message)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 其他消息处理逻辑...
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error("消息解码错误:", error)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 其他消息处理...
|
|
|
- }
|
|
|
- }
|
|
|
+ reader.readAsArrayBuffer(data)
|
|
|
+ },
|
|
|
|
|
|
- // WebRTC 连接
|
|
|
- const webrtcConnection = () => {
|
|
|
- if (!peer.value) return
|
|
|
-
|
|
|
- peer.value.onicecandidate = (e) => {
|
|
|
- if (e.candidate && socket.value) {
|
|
|
- const candidate = {
|
|
|
- type: 'answer_ice',
|
|
|
- iceCandidate: e.candidate
|
|
|
+ webrtcConnection() {
|
|
|
+ if (!this.peer) return
|
|
|
+
|
|
|
+ this.peer.onicecandidate = (e) => {
|
|
|
+ if (e.candidate && this.socket) {
|
|
|
+ const candidate = {
|
|
|
+ type: 'answer_ice',
|
|
|
+ iceCandidate: e.candidate
|
|
|
+ }
|
|
|
+ const message = {
|
|
|
+ content: JSON.stringify(candidate),
|
|
|
+ type: Constant.MESSAGE_TRANS_TYPE,
|
|
|
+ }
|
|
|
+ this.sendMessage(message)
|
|
|
}
|
|
|
- const message = {
|
|
|
- content: JSON.stringify(candidate),
|
|
|
- type: Constant.MESSAGE_TRANS_TYPE,
|
|
|
+ }
|
|
|
+
|
|
|
+ this.peer.ontrack = (e) => {
|
|
|
+ if (e && e.streams) {
|
|
|
+ const remoteVideo = document.getElementById("remoteVideoReceiver")
|
|
|
+ const remoteAudio = document.getElementById("audioPhone")
|
|
|
+
|
|
|
+ if (remoteVideo) remoteVideo.srcObject = e.streams[0]
|
|
|
+ if (remoteAudio) remoteAudio.srcObject = e.streams[0]
|
|
|
}
|
|
|
- sendMessage(message)
|
|
|
}
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- peer.value.ontrack = (e) => {
|
|
|
- if (e && e.streams) {
|
|
|
- const remoteVideo = document.getElementById("remoteVideoReceiver")
|
|
|
- const remoteAudio = document.getElementById("audioPhone")
|
|
|
-
|
|
|
- if (remoteVideo) remoteVideo.srcObject = e.streams[0]
|
|
|
- if (remoteAudio) remoteAudio.srcObject = e.streams[0]
|
|
|
+ reconnect() {
|
|
|
+ if (this.lockConnection || this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
|
+ return
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- // 处理WebRTC消息
|
|
|
- const dealWebRtcMessage = (messagePB) => {
|
|
|
- // 实现与React版本相同的逻辑
|
|
|
- }
|
|
|
+ this.lockConnection = true
|
|
|
+ this.reconnectAttempts++
|
|
|
+
|
|
|
+ console.log(`重新连接中... (尝试 ${this.reconnectAttempts}/${this.maxReconnectAttempts})`)
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ this.connect(localStorage.uuid)
|
|
|
+ this.lockConnection = false
|
|
|
+ }, this.reconnectInterval)
|
|
|
+ },
|
|
|
+
|
|
|
+ disconnect() {
|
|
|
+ if (this.socket) {
|
|
|
+ this.resetHeartbeat()
|
|
|
+ this.socket.close()
|
|
|
+ this.socket = null
|
|
|
+ }
|
|
|
+ if (this.peer) {
|
|
|
+ this.peer.close()
|
|
|
+ this.peer = null
|
|
|
+ }
|
|
|
+ this.reconnectAttempts = 0
|
|
|
+ },
|
|
|
|
|
|
- // 重新连接
|
|
|
- const reconnect = () => {
|
|
|
- if (lockConnection.value) return
|
|
|
- lockConnection.value = true
|
|
|
+ sendMessage(messageData) {
|
|
|
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
|
+ console.error("WebSocket未连接")
|
|
|
+ return false
|
|
|
+ }
|
|
|
|
|
|
- setTimeout(() => {
|
|
|
- if (socket.value?.readyState !== 1) {
|
|
|
- connect(localStorage.uuid)
|
|
|
+ const walletStore = useWalletStore()
|
|
|
+ const data = {
|
|
|
+ ...messageData,
|
|
|
+ fromUsername: walletStore.username,
|
|
|
+ from: walletStore.account,
|
|
|
}
|
|
|
- lockConnection.value = false
|
|
|
- }, 10000)
|
|
|
- }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const MessageType = protobuf.lookupType("protocol.Message")
|
|
|
+ const messagePB = MessageType.create(data)
|
|
|
+ const buffer = MessageType.encode(messagePB).finish()
|
|
|
+ this.socket.send(buffer)
|
|
|
+ return true
|
|
|
+ } catch (error) {
|
|
|
+ console.error("消息编码错误:", error)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- // 发送消息
|
|
|
- const sendMessage = (messageData) => {
|
|
|
- if (!socket.value) return
|
|
|
- const walletStore = useWalletStore()
|
|
|
- const data = {
|
|
|
- ...messageData,
|
|
|
- fromUsername: walletStore.username,
|
|
|
- from: walletStore.account,
|
|
|
+ dealWebRtcMessage(message) {
|
|
|
+ // 实现WebRTC消息处理逻辑
|
|
|
}
|
|
|
-
|
|
|
- const message = protobuf.lookup("protocol.Message")
|
|
|
- const messagePB = message.create(data)
|
|
|
- socket.value.send(message.encode(messagePB).finish())
|
|
|
- }
|
|
|
-
|
|
|
- // 关闭连接
|
|
|
- const close = () => {
|
|
|
- socket.value?.close()
|
|
|
- peer.value?.close()
|
|
|
- socket.value = null
|
|
|
- peer.value = null
|
|
|
- }
|
|
|
-
|
|
|
- return {
|
|
|
- socket,
|
|
|
- peer,
|
|
|
- connect,
|
|
|
- sendMessage,
|
|
|
- close,
|
|
|
- dealWebRtcMessage,
|
|
|
- webrtcConnection
|
|
|
}
|
|
|
})
|