socket-session.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import appSettings from '@/core/config/app-settings'
  2. const OPEN = 1
  3. const PING_INTERVAL = 30000
  4. const CONNECT_TIMEOUT = 8000
  5. export class SocketSession {
  6. constructor(handlers = {}) {
  7. this.handlers = handlers
  8. this.socket = null
  9. this.connecting = false
  10. this.connected = false
  11. this.allowReconnect = true
  12. this.paused = false
  13. this.reconnectAttempt = 0
  14. this.reconnecting = false
  15. this.heartbeatId = null
  16. this.connectTimer = null
  17. }
  18. open() {
  19. if (this.paused || !this.allowReconnect) return
  20. if (this.connecting) return
  21. if (this.connected && this.socket?.readyState === OPEN) return
  22. this.teardownSocket(false)
  23. this.connecting = true
  24. this.connected = false
  25. this.connectTimer = setTimeout(() => {
  26. if (!this.connected) this.handlers.onError?.()
  27. }, CONNECT_TIMEOUT)
  28. try {
  29. this.socket = uni.connectSocket({
  30. url: appSettings.socketurl,
  31. fail: () => {
  32. this.connecting = false
  33. this.clearConnectTimer()
  34. this.handlers.onError?.()
  35. }
  36. })
  37. this.socket.onOpen(() => {
  38. this.clearConnectTimer()
  39. this.connecting = false
  40. this.connected = true
  41. this.reconnectAttempt = 0
  42. this.startHeartbeat()
  43. this.handlers.onOpen?.()
  44. })
  45. this.socket.onMessage((msg) => {
  46. this.handlers.onMessage?.(msg)
  47. })
  48. this.socket.onClose((evt) => {
  49. this.onDisconnected(evt)
  50. })
  51. this.socket.onError(() => {
  52. this.clearConnectTimer()
  53. this.connecting = false
  54. this.connected = false
  55. this.handlers.onError?.()
  56. })
  57. } catch (err) {
  58. this.connecting = false
  59. this.clearConnectTimer()
  60. this.handlers.onError?.(err)
  61. }
  62. }
  63. send(data) {
  64. if (!this.socket || this.socket.readyState !== OPEN) return false
  65. try {
  66. const payload = typeof data === 'string' ? data : JSON.stringify(data)
  67. this.socket.send({ data: payload })
  68. return true
  69. } catch (_) {
  70. return false
  71. }
  72. }
  73. pause(temporary = true) {
  74. if (!temporary) {
  75. this.paused = true
  76. this.allowReconnect = false
  77. }
  78. this.stopHeartbeat()
  79. this.clearConnectTimer()
  80. if (this.socket) {
  81. try { this.socket.close() } catch (_) {}
  82. if (!temporary) this.socket = null
  83. }
  84. this.connecting = false
  85. this.connected = false
  86. }
  87. resume() {
  88. if (this.paused && !this.allowReconnect) {
  89. this.paused = false
  90. this.allowReconnect = true
  91. }
  92. this.open()
  93. }
  94. scheduleReconnect() {
  95. if (!this.allowReconnect || this.paused || this.reconnecting) return
  96. if (this.reconnectAttempt >= 10) {
  97. this.reconnectAttempt = 0
  98. return
  99. }
  100. this.reconnecting = true
  101. const delay = Math.min(1000 * Math.pow(1.5, this.reconnectAttempt), 5000)
  102. setTimeout(() => {
  103. this.reconnecting = false
  104. if (!this.allowReconnect || this.paused) return
  105. this.reconnectAttempt += 1
  106. this.open()
  107. }, delay)
  108. }
  109. startHeartbeat() {
  110. this.stopHeartbeat()
  111. this.heartbeatId = setInterval(() => {
  112. if (!this.socket || this.socket.readyState !== OPEN) return
  113. try {
  114. this.socket.send({
  115. data: '{"type":"ping"}',
  116. fail: () => this.handlers.onError?.()
  117. })
  118. } catch (_) {
  119. this.handlers.onError?.()
  120. }
  121. }, PING_INTERVAL)
  122. }
  123. stopHeartbeat() {
  124. if (this.heartbeatId) {
  125. clearInterval(this.heartbeatId)
  126. this.heartbeatId = null
  127. }
  128. }
  129. clearConnectTimer() {
  130. if (this.connectTimer) {
  131. clearTimeout(this.connectTimer)
  132. this.connectTimer = null
  133. }
  134. }
  135. onDisconnected(evt) {
  136. this.connecting = false
  137. this.connected = false
  138. this.stopHeartbeat()
  139. this.clearConnectTimer()
  140. this.handlers.onClose?.(evt)
  141. if (evt?.code === 1000 && this.paused) {
  142. this.socket = null
  143. return
  144. }
  145. this.scheduleReconnect()
  146. }
  147. teardownSocket(clearRef) {
  148. this.stopHeartbeat()
  149. this.clearConnectTimer()
  150. if (this.socket) {
  151. try { this.socket.close() } catch (_) {}
  152. if (clearRef) this.socket = null
  153. }
  154. }
  155. }