HsSocket.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import io from 'socket.io-client'
  2. import { setBaseOption, BaseOption } from './storage'
  3. import { type SocketOptions, SocketEvent } from './type'
  4. import { SocketStatus, HskTerminatedCode } from './outputType'
  5. import EventEmitter from '../eventemitter'
  6. import { Logger } from '../Logger'
  7. class HsSocket extends EventEmitter {
  8. private logger: Logger
  9. public socket?: typeof io.Socket
  10. /** 初始化 socket 需要的参数 */
  11. public socketOptions: SocketOptions
  12. /** 心跳延迟时间 */
  13. private heartBeatDelay: number
  14. /** 主动关闭链接时间 */
  15. private closeHeartBeatDelay: number
  16. /** 心跳检测定时器 */
  17. private heartBeatTimer: null | ReturnType<typeof setTimeout>
  18. /** 清空心跳检测定时器 */
  19. private closeHeartBeatTimer: null | ReturnType<typeof setTimeout>
  20. /** 超时次数 */
  21. // private timeOutCount: number
  22. /** 最大超时次数限制 */
  23. private imRetryCount: number
  24. /** 本次 socket 会话唯一 id */
  25. private sessionId: string
  26. constructor(socketOptions: SocketOptions) {
  27. super()
  28. /** 页面关闭时 关闭 socket */
  29. window.addEventListener('onunload', this.closeSocket)
  30. this.logger = new Logger(socketOptions.loggerLevel, 'HsSocket')
  31. this.socket = undefined
  32. this.heartBeatTimer = null
  33. this.closeHeartBeatTimer = null
  34. // this.timeOutCount = 0
  35. this.sessionId = ''
  36. this.socketOptions = socketOptions
  37. this.heartBeatDelay = socketOptions.imHeartTime * 1000 || 3000
  38. this.closeHeartBeatDelay = socketOptions.imHeartTime * 1000 || 3000
  39. this.imRetryCount = socketOptions.imRetryCount || 10
  40. }
  41. /** @public initSocket 初始化 Socket 连接 */
  42. public initSocket(): void {
  43. /** 如果有未断开的连接先断开 */
  44. this.closeSocket()
  45. // 设置状态为连接中
  46. this.emit(SocketEvent.SetSocketStatus, {
  47. status: SocketStatus.Connecting
  48. })
  49. /** https://socket.io/docs/v2/client-api/#iourl */
  50. this.socket = io(this.socketOptions.imWsServer, {
  51. transports: ['websocket'],
  52. reconnectionAttempts: this.imRetryCount,
  53. reconnectionDelay: this.heartBeatDelay,
  54. reconnectionDelayMax: this.heartBeatDelay + 1000,
  55. timeout: this.heartBeatDelay,
  56. reconnection: true
  57. })
  58. /** https://socket.io/docs/v2/client-api/#event-error */
  59. this.socket.on('error', (error: object) => {
  60. this.logger.error(`socket_error | ${JSON.stringify(error)}`)
  61. })
  62. /** https://socket.io/docs/v2/client-api/#event-connect_error-1 */
  63. this.socket.on('connect_error', (error: object) => {
  64. const errorData = `socket_connect_error | ${JSON.stringify(error)}`
  65. this.logger.warn(errorData)
  66. })
  67. this.socket.on('reconnecting', (res: string) => {
  68. this.logger.error(`socket_warn | socket_reconnecting | ${res}`)
  69. this.emit(SocketEvent.SetSocketStatus, {
  70. status: SocketStatus.ReTry
  71. })
  72. })
  73. this.socket.on('reconnect', (res: string) => {
  74. this.logger.error(`socket_warn | socket_reconnect | ${res}`)
  75. this.emit(SocketEvent.SetSocketStatus, {
  76. status: SocketStatus.Ready
  77. })
  78. })
  79. this.socket.on('reconnect_failed', (error: string) => {
  80. this.logger.error(`socket_warn | socket_reconnect_failed | ${error}`)
  81. this.emit(SocketEvent.SetSocketStatus, {
  82. status: SocketStatus.Terminated,
  83. code: HskTerminatedCode.SocketOnReconnectFailed,
  84. error: `${error}`
  85. })
  86. })
  87. /** https://socket.io/docs/v2/client-api/#event-connect */
  88. this.socket.on('connect', () => {
  89. this.emit(SocketEvent.SetSocketStatus, {
  90. status: SocketStatus.Connected
  91. })
  92. this.socketLogin()
  93. })
  94. /** https://socket.io/docs/v2/client-api/#event-disconnect */
  95. this.socket.on('disconnect', (reason: string) => {
  96. const errorMessage = `socket_disconnect | ${reason}`
  97. this.logger.warn(errorMessage)
  98. })
  99. /** 服务端下行事件 */
  100. this.socket.on('common_down_data', (e: string) => {
  101. // console.log(e, 3434343434)
  102. if (e && JSON.parse(e) && JSON.parse(e).data) {
  103. this.emit(SocketEvent.SocketDownEvent, {
  104. eventData: JSON.parse(JSON.parse(e).data)
  105. })
  106. }
  107. })
  108. /** 服务端下行指令 */
  109. this.socket.on('common_down_cmd', (e: string) => {
  110. const { clientSessionId } = JSON.parse(e)
  111. // console.log('dsdsdsdsdsds', clientSessionId)
  112. if (clientSessionId === this.sessionId) {
  113. this.logger.error(
  114. `socket status | ${SocketStatus.Terminated} | 坐席在其他页面重新初始化,本页面被踢出`
  115. )
  116. this.emit(SocketEvent.SetSocketStatus, {
  117. status: SocketStatus.Terminated,
  118. code: HskTerminatedCode.SocketRepeatLogin,
  119. error: '您已在其他页面签入,当前页面连接已断开'
  120. })
  121. }
  122. })
  123. }
  124. /** @private socketLogin 客户端上行登录事件 */
  125. private socketLogin() {
  126. const data = {
  127. // appCode: this.socketOptions.appCode || '1111',
  128. // token: this.socketOptions.token || '1111',
  129. userId: this.socketOptions.agent_id
  130. }
  131. this.socket &&
  132. this.socket.emit('login', data, (sessionId: string) => {
  133. // console.log(sessionId, '测试一下')
  134. this.emit(SocketEvent.SetSocketStatus, {
  135. status: SocketStatus.Ready
  136. })
  137. setBaseOption(BaseOption.TrackParams, { socket_session_id: sessionId })
  138. this.sessionId = sessionId
  139. this.startHeartbeat()
  140. })
  141. }
  142. /** @public closeSocket 关闭 socket 连接 */
  143. public closeSocket() {
  144. if (this.socket) {
  145. this.socket.io.opts.reconnection = false
  146. this.socket.close()
  147. // 清除之前的监听事件
  148. this.socket.removeAllListeners()
  149. }
  150. this.socket = undefined
  151. this.sessionId = ''
  152. if (this.heartBeatTimer) {
  153. window.clearTimeout(this.heartBeatTimer)
  154. this.heartBeatTimer = null
  155. }
  156. if (this.closeHeartBeatTimer) {
  157. window.clearTimeout(this.closeHeartBeatTimer)
  158. this.closeHeartBeatTimer = null
  159. }
  160. }
  161. /** @private startHeartbeat 开启心跳检测 */
  162. private startHeartbeat() {
  163. if (this.heartBeatTimer) {
  164. window.clearTimeout(this.heartBeatTimer)
  165. this.heartBeatTimer = null
  166. }
  167. if (this.closeHeartBeatTimer) {
  168. window.clearTimeout(this.closeHeartBeatTimer)
  169. this.closeHeartBeatTimer = null
  170. }
  171. this.socket && this.heartbeatEvent()
  172. }
  173. /** @private heartbeatEvent websocket心跳检测 */
  174. private heartbeatEvent() {
  175. this.heartBeatTimer = setTimeout(() => {
  176. this.socket && this.sendHeartbeat()
  177. /** 如果心跳检测一直没回应,则进行重连 */
  178. this.closeHeartBeatTimer = setTimeout(() => {
  179. this.logger.warn('socket_heart_beat | 心跳超时,即将重新连接')
  180. this.initSocket()
  181. }, this.closeHeartBeatDelay)
  182. }, this.heartBeatDelay)
  183. }
  184. /** @private sendHeartbeat 客户端上行心跳事件 */
  185. private sendHeartbeat() {
  186. this.socket &&
  187. this.socket.emit(
  188. 'heartbeat',
  189. JSON.stringify({
  190. ...this.socketOptions
  191. }),
  192. () => {
  193. this.startHeartbeat()
  194. }
  195. )
  196. }
  197. }
  198. export default HsSocket