wsManager.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. const CC_WECHATGAME = false
  2. /**
  3. * Initialize a new `Emitter`.
  4. *
  5. * @api public
  6. */
  7. function Emitter (obj) {
  8. if (obj) return mixin(obj)
  9. };
  10. /**
  11. * Mixin the emitter properties.
  12. *
  13. * @param {Object} obj
  14. * @return {Object}
  15. * @api private
  16. */
  17. function mixin (obj) {
  18. for (var key in Emitter.prototype) {
  19. obj[key] = Emitter.prototype[key]
  20. }
  21. return obj
  22. }
  23. /**
  24. * Listen on the given `event` with `fn`.
  25. *
  26. * @param {String} event
  27. * @param {Function} fn
  28. * @return {Emitter}
  29. * @api public
  30. */
  31. Emitter.prototype.on =
  32. Emitter.prototype.addEventListener = function (event, fn) {
  33. this._callbacks = this._callbacks || {};
  34. (this._callbacks['$' + event] = this._callbacks['$' + event] || [])
  35. .push(fn)
  36. return this
  37. }
  38. /**
  39. * Adds an `event` listener that will be invoked a single
  40. * time then automatically removed.
  41. *
  42. * @param {String} event
  43. * @param {Function} fn
  44. * @return {Emitter}
  45. * @api public
  46. */
  47. Emitter.prototype.once = function (event, fn) {
  48. function on () {
  49. this.off(event, on)
  50. fn.apply(this, arguments)
  51. }
  52. on.fn = fn
  53. this.on(event, on)
  54. return this
  55. }
  56. /**
  57. * Remove the given callback for `event` or all
  58. * registered callbacks.
  59. *
  60. * @param {String} event
  61. * @param {Function} fn
  62. * @return {Emitter}
  63. * @api public
  64. */
  65. Emitter.prototype.off =
  66. Emitter.prototype.removeListener =
  67. Emitter.prototype.removeAllListeners =
  68. Emitter.prototype.removeEventListener = function (event, fn) {
  69. this._callbacks = this._callbacks || {}
  70. // all
  71. if (arguments.length === 0) {
  72. this._callbacks = {}
  73. return this
  74. }
  75. // specific event
  76. var callbacks = this._callbacks['$' + event]
  77. if (!callbacks) return this
  78. // remove all handlers
  79. if (arguments.length === 1) {
  80. delete this._callbacks['$' + event]
  81. return this
  82. }
  83. // remove specific handler
  84. var cb
  85. for (var i = 0; i < callbacks.length; i++) {
  86. cb = callbacks[i]
  87. if (cb === fn || cb.fn === fn) {
  88. callbacks.splice(i, 1)
  89. break
  90. }
  91. }
  92. return this
  93. }
  94. /**
  95. * Emit `event` with the given args.
  96. *
  97. * @param {String} event
  98. * @param {Mixed} ...
  99. * @return {Emitter}
  100. */
  101. Emitter.prototype.emit = function (event) {
  102. this._callbacks = this._callbacks || {}
  103. var args = [].slice.call(arguments, 1)
  104. var callbacks = this._callbacks['$' + event]
  105. if (callbacks) {
  106. callbacks = callbacks.slice(0)
  107. for (var i = 0, len = callbacks.length; i < len; ++i) {
  108. callbacks[i].apply(this, args)
  109. }
  110. }
  111. return this
  112. }
  113. /**
  114. * Return array of callbacks for `event`.
  115. *
  116. * @param {String} event
  117. * @return {Array}
  118. * @api public
  119. */
  120. Emitter.prototype.listeners = function (event) {
  121. this._callbacks = this._callbacks || {}
  122. return this._callbacks['$' + event] || []
  123. }
  124. /**
  125. * Check if this emitter has `event` handlers.
  126. *
  127. * @param {String} event
  128. * @return {Boolean}
  129. * @api public
  130. */
  131. Emitter.prototype.hasListeners = function (event) {
  132. return !!this.listeners(event).length
  133. }
  134. // function bind (obj, fn) {
  135. // if (typeof fn === 'string') fn = obj[fn]
  136. // if (typeof fn !== 'function') throw new Error('bind() requires a function')
  137. // var args = [].slice.call(arguments, 2)
  138. // return function () {
  139. // return fn.apply(obj, args.concat([].slice.call(arguments)))
  140. // }
  141. // };
  142. /**
  143. * WebSocket管理器
  144. * 支持最多同时实例化5个socket连接(微信环境限制目前最多为5个)
  145. * @param {String} url socket地址
  146. * @param {Object} opts 配置
  147. *
  148. */
  149. function WsManager (url, opts) {
  150. if (!(this instanceof WsManager)) {
  151. return new WsManager(url, opts)
  152. }
  153. if (url && (typeof url === 'object')) {
  154. opts = url
  155. url = undefined
  156. }
  157. Emitter(this)
  158. opts = opts || {}
  159. opts.path = opts.path || '/'
  160. this.opts = opts
  161. this.url = url
  162. this.lastPing = null
  163. this.socketCache = [] // 缓存socket队列
  164. this.socketMaxCache = 5 // 最大缓存socket实例数量 [暂时没有使用]
  165. this.readyState = 'closed' // 当前socket状态
  166. this.binaryType = opts.binaryType || 'blob' // 数据传输类型
  167. this._reconnectTimes = 0 // 重连次数
  168. this._reconnectionDelay = opts.reconnectionDelay || 1000 // 重连延迟
  169. this.reconnection(opts.reconnection !== false) // 是否自动重连
  170. this.reconnectionAttempts(opts.reconnectionAttempts || Infinity) // 重连最大尝试次数
  171. this.timeout(opts.timeout === null ? 20000 : opts.timeout)
  172. this.logStyle = 'color:blue; font-size:16px;font-weight:bold;'
  173. this.keepAliveInterval = 30000 // 心跳包发送间隔
  174. this.keepAliveTimeout = null // 心跳包计时器
  175. this.keepAliveContent = opts.keepAliveContent || 1 // 心跳包内容
  176. this.autoConnect = opts.autoConnect !== false // 是否自动连接
  177. if (this.autoConnect) {
  178. this.connect()
  179. }
  180. }
  181. WsManager.prototype.connect = function (fn) {
  182. if (~this.readyState.indexOf('open')) {
  183. return this
  184. }
  185. this.readyState = 'opening'
  186. let _socket = CC_WECHATGAME ? this.openWxConnect() : this.openH5Connect()
  187. this.socketCache.push(_socket)
  188. this.socket = _socket
  189. }
  190. WsManager.prototype.reconnect = function () {
  191. clearInterval(this.keepAliveTimeout)
  192. if (this.readyState === 'open' || this.readyState === 'opening') {
  193. } else {
  194. if (this._reconnectTimes < this._reconnectionAttempts) {
  195. if (this.socket) {
  196. this.socket.close()
  197. }
  198. this._reconnectTimes += 1
  199. this.readyState = 'reconnecting'
  200. // console.log(`%c [Socket正在尝试第${this._reconnectTimes}次重连]`, this.logStyle);
  201. setTimeout(() => {
  202. this.socket = CC_WECHATGAME ? this.openWxConnect() : this.openH5Connect()
  203. }, this._reconnectionDelay)
  204. } else {
  205. // console.log(`%c [达到最大连续重连失败次数,已重置,30秒后重试]`, this.logStyle);
  206. if (this.socket) {
  207. this.socket.close()
  208. }
  209. this._reconnectTimes = 1
  210. this.readyState = 'reconnecting'
  211. setTimeout(() => {
  212. this.socket = CC_WECHATGAME ? this.openWxConnect() : this.openH5Connect()
  213. }, 30000)
  214. }
  215. }
  216. }
  217. WsManager.prototype.destroy = function () {
  218. this._reconnection = false
  219. this.socket.close()
  220. }
  221. WsManager.prototype.keepAlive = function () {
  222. this.keepAliveTimeout = setInterval(() => {
  223. if (this.readyState === 'open') {
  224. // let payload = {
  225. // type: 6
  226. // }
  227. // let msg = Message.create(payload);
  228. // let buffer = Message.encode(msg).finish()
  229. // this.send(buffer);
  230. this.send(this.keepAliveContent)
  231. } else if (this.readyState === 'closed') {
  232. this.reconnect()
  233. }
  234. }, this.keepAliveInterval)
  235. }
  236. WsManager.prototype.openWxConnect = function () {
  237. let _header = {}
  238. let _socket = wx.connectSocket({
  239. url: this.url,
  240. header: _header,
  241. success: function (ret) {}
  242. })
  243. _socket.onOpen((res) => {
  244. this.readyState = 'open'
  245. this.emit('open', res)
  246. // console.log(`%c [Socket连接成功: ${this.url.split("&token")[0]}]`, this.logStyle);
  247. // 连接成功,重置重连次数
  248. this._reconnectTimes = 1
  249. // 每隔一段时间发一个心跳包保持连接状态
  250. this.keepAlive()
  251. })
  252. _socket.onClose((res) => {
  253. // console.log(`%c [Socket连接被关闭: ${res}]`, this.logStyle)
  254. clearInterval(this.keepAliveTimeout)
  255. this.readyState = 'closed'
  256. // 只要关闭就重连(暂时性处理)
  257. if (this._reconnection) {
  258. this.reconnect()
  259. }
  260. this.emit('close', res)
  261. })
  262. _socket.onMessage((res) => {
  263. if (res.data !== 1) {
  264. this.emit('message', res.data)
  265. }
  266. // console.log(`%c [接收到Socket消息: ${JSON.stringify(res.data)}]`, this.logStyle);
  267. })
  268. _socket.onError((res) => {
  269. // console.log(`%c [Socket错误: ${res.errMsg}]`, this.logStyle);
  270. clearInterval(this.keepAliveTimeout)
  271. if (this._reconnection) {
  272. if (this.readyState !== 'reconnecting') {
  273. this.readyState = 'error'
  274. this.reconnect()
  275. }
  276. } else {
  277. _socket.close()
  278. }
  279. this.emit('error', res.errMsg)
  280. })
  281. return _socket
  282. }
  283. WsManager.prototype.openH5Connect = function () {
  284. let _socket = new WebSocket(this.url)
  285. // _socket.binaryType = "arraybuffer";
  286. _socket.onopen = (event) => {
  287. this.readyState = 'open'
  288. this.emit('open', event)
  289. // console.log(`%c [Socket连接成功: ${this.url.split("&token")[0]}]`, this.logStyle);
  290. // 连接成功,重置重连次数
  291. this._reconnectTimes = 1
  292. // 每隔一段时间发一个心跳包保持连接状态
  293. this.keepAlive()
  294. }
  295. _socket.onclose = (event) => {
  296. clearInterval(this.keepAliveTimeout)
  297. this.readyState = 'closed'
  298. // let code = event.code
  299. // let reason = event.reason
  300. // let wasClean = event.wasClean
  301. // console.log(`%c [Socket连接被关闭: ${reason}]`, this.logStyle)
  302. // 只要关闭就重连(暂时性处理)
  303. if (this._reconnection) {
  304. this.reconnect()
  305. }
  306. this.emit('close', event)
  307. }
  308. _socket.onmessage = (event) => {
  309. if (event.data !== 1) {
  310. this.emit('message', event.data)
  311. }
  312. // console.log(`%c [接收到Socket消息: ${event.data}]`, this.logStyle);
  313. }
  314. _socket.onerror = (event) => {
  315. // console.log(`%c [Socket错误: ${JSON.stringify(event)}]`, this.logStyle);
  316. clearInterval(this.keepAliveTimeout)
  317. if (this._reconnection) {
  318. if (this.readyState !== 'reconnecting') {
  319. this.readyState = 'error'
  320. this.reconnect()
  321. }
  322. } else {
  323. _socket.close()
  324. }
  325. this.emit(event)
  326. }
  327. return _socket
  328. }
  329. WsManager.prototype.send = function (data) {
  330. // console.log(`%c [发送Socket数据: ${data}]`, this.logStyle);
  331. if (CC_WECHATGAME) {
  332. let buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
  333. this.socket.send({
  334. data: buffer,
  335. success: function (res) {
  336. // console.log('Success: ', JSON.stringify(res));
  337. },
  338. fail: function (res) {
  339. // console.log('Fail: ', JSON.stringify(res));
  340. },
  341. complete: function (res) {
  342. // console.log('Complete: ', JSON.stringify(res));
  343. }
  344. })
  345. } else {
  346. this.socket.binaryType = this.binaryType
  347. this.socket.send(data)
  348. }
  349. }
  350. /**
  351. * 配置socket连接超时时间
  352. * @param {Number} v 连接超时时间
  353. * @return {WsManager} WsManager对象实例
  354. * @api public
  355. */
  356. WsManager.prototype.timeout = function (v) {
  357. if (!arguments.length) {
  358. return this._timeout
  359. }
  360. this._timeout = v
  361. return this
  362. }
  363. /**
  364. * 自动重连配置
  365. * @param {Boolean} v 是否自动重连true / false
  366. * @return {WsManager} WsManager对象实例
  367. * @api public
  368. */
  369. WsManager.prototype.reconnection = function (v) {
  370. if (!arguments.length) {
  371. return this._reconnection
  372. }
  373. this._reconnection = !!v
  374. return this
  375. }
  376. /**
  377. * 配置最大重连次数
  378. * @param {Number} v 最大重连次数
  379. * @return {WsManager} WsManager对象实例
  380. * @api public
  381. */
  382. WsManager.prototype.reconnectionAttempts = function (v) {
  383. if (!arguments.length) {
  384. return this._reconnectionAttempts
  385. }
  386. this._reconnectionAttempts = v
  387. return this
  388. }
  389. export default WsManager