webSocketStore.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. // src/stores/websocket.ts
  2. import { defineStore } from "pinia";
  3. import { $root as protobuf } from "@/common/proto/proto";
  4. import * as Constant from "@/common/constant/Constant";
  5. import { useWalletStore } from "@/stores/modules/walletStore";
  6. export const useWebSocketStore = defineStore("webSocketStore", {
  7. // 状态定义
  8. state: () => ({
  9. socket: null, // socket实例
  10. peer: null, // peer实例
  11. lockConnection: false, // 锁定连接
  12. reconnectAttempts: 0, // 重连次数
  13. maxReconnectAttempts: 5, // 最大重连次数
  14. reconnectInterval: 3000, // 重连间隔
  15. messages: [], // 消息列表
  16. unreadMessages: [], // 未读消息列表
  17. lastMessage: null, // 最后一条消息
  18. // 心跳检测配置
  19. heartCheck: {
  20. timeout: 10000, // 连接丢失后,多长时间内没有收到服务端的消息,则认为连接已断开
  21. timeoutObj: null, // 定时器对象
  22. serverTimeoutObj: null, // 服务器定时器对象
  23. num: 3, // 重连次数
  24. },
  25. }),
  26. // 计算属性
  27. getters: {
  28. isConnected: (state) => state.socket?.readyState === WebSocket.OPEN,
  29. hasUnreadMessages: (state) => state.unreadMessages.length > 0,
  30. },
  31. // 方法
  32. actions: {
  33. // 初始化socket
  34. startHeartbeat() {
  35. const self = this;
  36. const _num = this.heartCheck.num;
  37. this.heartCheck.timeoutObj && clearTimeout(this.heartCheck.timeoutObj);
  38. this.heartCheck.serverTimeoutObj &&
  39. clearTimeout(this.heartCheck.serverTimeoutObj);
  40. this.heartCheck.timeoutObj = setTimeout(() => {
  41. if (this.socket?.readyState === WebSocket.OPEN) {
  42. const data = {
  43. type: "heatbeat",
  44. content: "ping",
  45. };
  46. const MessageType = protobuf.lookupType("protocol.Message");
  47. const messagePB = MessageType.create(data);
  48. const buffer = MessageType.encode(messagePB).finish();
  49. this.socket.send(buffer);
  50. }
  51. self.heartCheck.serverTimeoutObj = setTimeout(() => {
  52. _num--;
  53. if (_num <= 0) {
  54. console.log("the ping num is more then 3, close socket!");
  55. this.socket?.close();
  56. }
  57. }, self.heartCheck.timeout);
  58. }, this.heartCheck.timeout);
  59. },
  60. resetHeartbeat() {
  61. this.heartCheck.timeoutObj && clearTimeout(this.heartCheck.timeoutObj);
  62. this.heartCheck.serverTimeoutObj &&
  63. clearTimeout(this.heartCheck.serverTimeoutObj);
  64. this.heartCheck.num = 3;
  65. },
  66. // 链接ws
  67. connect(userUuid) {
  68. console.log("开始连接...");
  69. this.disconnect(); // 确保先断开现有连接
  70. this.peer = new RTCPeerConnection();
  71. this.socket = new WebSocket(
  72. `ws://192.168.0.59:8888/api/v1/socket.io?user=${userUuid}`
  73. );
  74. this.socket.onopen = () => {
  75. this.startHeartbeat();
  76. console.log("WebSocket连接成功");
  77. this.webrtcConnection();
  78. };
  79. this.socket.onmessage = (event) => {
  80. this.startHeartbeat();
  81. this.handleMessage(event.data);
  82. };
  83. this.socket.onclose = () => {
  84. console.log("连接关闭,尝试重新连接...");
  85. this.resetHeartbeat();
  86. this.reconnect();
  87. };
  88. this.socket.onerror = (error) => {
  89. console.error("WebSocket错误:", error);
  90. this.resetHeartbeat();
  91. this.reconnect();
  92. };
  93. },
  94. // 处理消息
  95. handleMessage(data) {
  96. const MessageType = protobuf.lookupType("protocol.Message");
  97. const reader = new FileReader();
  98. reader.onload = (event) => {
  99. try {
  100. const messagePB = MessageType.decode(
  101. new Uint8Array(event.target?.result)
  102. );
  103. const message = MessageType.toObject(messagePB, {
  104. longs: String,
  105. enums: String,
  106. bytes: String,
  107. });
  108. console.log("收到消息:", message);
  109. // 更新状态
  110. this.messages.push(message);
  111. this.lastMessage = message;
  112. if (message.type === "heatbeat") return;
  113. if (message.type === Constant.MESSAGE_TRANS_TYPE) {
  114. this.dealWebRtcMessage(message);
  115. return;
  116. }
  117. // 其他消息处理逻辑...
  118. } catch (error) {
  119. console.error("消息解码错误:", error);
  120. }
  121. };
  122. reader.readAsArrayBuffer(data);
  123. },
  124. // 处理WebRTC消息
  125. webrtcConnection() {
  126. if (!this.peer) return;
  127. this.peer.onicecandidate = (e) => {
  128. if (e.candidate && this.socket) {
  129. const candidate = {
  130. type: "answer_ice",
  131. iceCandidate: e.candidate,
  132. };
  133. const message = {
  134. content: JSON.stringify(candidate),
  135. type: Constant.MESSAGE_TRANS_TYPE,
  136. };
  137. this.sendMessage(message);
  138. }
  139. };
  140. this.peer.ontrack = (e) => {
  141. if (e && e.streams) {
  142. const remoteVideo = document.getElementById("remoteVideoReceiver");
  143. const remoteAudio = document.getElementById("audioPhone");
  144. if (remoteVideo) remoteVideo.srcObject = e.streams[0];
  145. if (remoteAudio) remoteAudio.srcObject = e.streams[0];
  146. }
  147. };
  148. },
  149. // 断线重连
  150. reconnect() {
  151. if (
  152. this.lockConnection ||
  153. this.reconnectAttempts >= this.maxReconnectAttempts
  154. ) {
  155. return;
  156. }
  157. this.lockConnection = true;
  158. this.reconnectAttempts++;
  159. console.log(
  160. `重新连接中... (尝试 ${this.reconnectAttempts}/${this.maxReconnectAttempts})`
  161. );
  162. setTimeout(() => {
  163. this.connect(localStorage.uuid);
  164. this.lockConnection = false;
  165. }, this.reconnectInterval);
  166. },
  167. // 重连
  168. disconnect() {
  169. if (this.socket) {
  170. this.resetHeartbeat();
  171. this.socket.close();
  172. this.socket = null;
  173. }
  174. if (this.peer) {
  175. this.peer.close();
  176. this.peer = null;
  177. }
  178. this.reconnectAttempts = 0;
  179. },
  180. // 发送消息
  181. sendMessage(messageData) {
  182. if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
  183. console.error("WebSocket未连接");
  184. return false;
  185. }
  186. const walletStore = useWalletStore();
  187. const data = {
  188. ...messageData,
  189. fromUsername: walletStore.username,
  190. from: walletStore.account,
  191. };
  192. try {
  193. const MessageType = protobuf.lookupType("protocol.Message");
  194. const messagePB = MessageType.create(data);
  195. const buffer = MessageType.encode(messagePB).finish();
  196. this.socket.send(buffer);
  197. return true;
  198. } catch (error) {
  199. console.error("消息编码错误:", error);
  200. return false;
  201. }
  202. },
  203. // 发送WebRTC消息
  204. dealWebRtcMessage(message) {
  205. // 实现WebRTC消息处理逻辑
  206. },
  207. },
  208. });