ts-sdk

index.ts 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. import { ReadyStateCallback, RequestCallback } from './callback';
  2. import { Packet } from './packet';
  3. import { Utils } from './utils';
  4. const MAX_PAYLOAD = 1024 * 1024;
  5. /**
  6. * Client ws client, 单例模式, 负责维护连接
  7. */
  8. class Client {
  9. private listeners: Map<number, (data: string) => void>;
  10. private requestCallback: RequestCallback;
  11. private requestHeader: string;
  12. private responseHeader: string;
  13. private maxPayload: number;
  14. private url: string;
  15. private reconnectTimes: number;
  16. private reconnectLock: boolean;
  17. private socket: WebSocket;
  18. private readyStateCallback: ReadyStateCallback;
  19. constructor(url: string, readyStateCallback: ReadyStateCallback) {
  20. this.listeners = new Map<number, (data: string) => void>();
  21. this.maxPayload = MAX_PAYLOAD;
  22. this.url = url;
  23. this.readyStateCallback = readyStateCallback;
  24. this.socket = this.connect();
  25. }
  26. // 向服务端发送ping包保持长连接
  27. ping(param = {}, requestCallback: RequestCallback) {
  28. if (this.socket.readyState !== this.socket.OPEN) {
  29. throw new Error('asyncSend: connection refuse');
  30. }
  31. this.listeners.set(
  32. 0,
  33. (data: string): void => {
  34. const code = this.getResponseProperty('code');
  35. if (code !== '') {
  36. const message = this.getResponseProperty('message');
  37. requestCallback.onError(Number(code), message);
  38. } else {
  39. requestCallback.onSuccess(data);
  40. }
  41. requestCallback.onEnd();
  42. },
  43. );
  44. const p = new Packet();
  45. this.send(p.pack(0, 0, this.requestHeader, JSON.stringify(param)));
  46. }
  47. send(data) {
  48. if (this.socket.readyState !== this.socket.OPEN) {
  49. console.error('WebSocket is already in CLOSING or CLOSED state.');
  50. return;
  51. }
  52. try {
  53. this.socket.send(data);
  54. } catch (e) {
  55. console.log('send data error', e);
  56. }
  57. }
  58. /**
  59. * asyncSend
  60. * @param {*} operator
  61. * @param {*} param
  62. * @param {*} callback 仅此次有效的callback
  63. */
  64. asyncSend(operator, param, callback) {
  65. console.info('websocket send data', operator, this.requestHeader, param);
  66. if (typeof callback !== 'object') {
  67. throw new Error('callback must be an object');
  68. }
  69. if (this.socket.readyState !== this.socket.OPEN) {
  70. throw new Error('asyncSend: connection refuse');
  71. }
  72. if (
  73. callback.hasOwnProperty('onStart') &&
  74. typeof callback.onStart === 'function'
  75. ) {
  76. callback.onStart();
  77. }
  78. let sequence = new Date().getTime();
  79. let listener = Utils.crc32(operator) + sequence;
  80. this.requestCallback[listener] = (data) => {
  81. let code = this.getResponseProperty('code');
  82. if (typeof code !== 'undefined') {
  83. let message = this.getResponseProperty('message');
  84. if (
  85. callback.hasOwnProperty('onError') &&
  86. typeof callback.onError === 'function'
  87. ) {
  88. callback.onError(code, message);
  89. }
  90. } else {
  91. if (
  92. callback.hasOwnProperty('onSuccess') &&
  93. typeof callback.onSuccess === 'function'
  94. ) {
  95. callback.onSuccess(data);
  96. }
  97. }
  98. if (
  99. callback.hasOwnProperty('onEnd') &&
  100. typeof callback.onEnd === 'function'
  101. ) {
  102. callback.onEnd();
  103. }
  104. delete this.requestCallback[listener];
  105. };
  106. const p = new Packet();
  107. this.send(
  108. p.pack(
  109. Utils.crc32(operator),
  110. sequence,
  111. this.requestHeader,
  112. JSON.stringify(param),
  113. ),
  114. );
  115. }
  116. // 同步请求服务端数据
  117. async syncSend(operator, param, callback) {
  118. await this.asyncSend(operator, param, callback);
  119. }
  120. // 添加消息监听
  121. addMessageListener(operator: string, listener: (data: string) => void) {
  122. this.listeners[Utils.crc32(operator)] = listener;
  123. }
  124. // 移除消息监听
  125. removeMessageListener(operator: string) {
  126. delete this.listeners[Utils.crc32(operator)];
  127. }
  128. // 获取socket的链接状态
  129. getReadyState() {
  130. return this.socket.readyState;
  131. }
  132. // 设置单个请求能够处理的最大字节数
  133. setMaxPayload(maxPayload) {
  134. this.maxPayload = maxPayload;
  135. }
  136. // 设置请求属性
  137. setRequestProperty(key, value) {
  138. let v = this.getRequestProperty(key);
  139. this.requestHeader = this.requestHeader.replace(key + '=' + v + ';', '');
  140. this.requestHeader = this.requestHeader + key + '=' + value + ';';
  141. }
  142. // 获取请求属性
  143. getRequestProperty(key) {
  144. let values = this.requestHeader.split(';');
  145. for (let index in values) {
  146. let kv = values[index].split('=');
  147. if (kv[0] === key) {
  148. return kv[1];
  149. }
  150. }
  151. }
  152. // 设置Response属性
  153. setResponseProperty(key, value) {
  154. let v = this.getResponseProperty(key);
  155. this.responseHeader = this.responseHeader.replace(key + '=' + v + ';', '');
  156. this.responseHeader = this.responseHeader + key + '=' + value + ';';
  157. }
  158. // 获取响应属性
  159. getResponseProperty(key): string {
  160. let values = this.responseHeader.split(';');
  161. for (let index in values) {
  162. let kv = values[index].split('=');
  163. if (kv[0] === key) {
  164. return kv[1];
  165. }
  166. }
  167. return '';
  168. }
  169. // 创建连接
  170. connect(): WebSocket {
  171. const readyStateCallback = this.readyStateCallback;
  172. let ws = new WebSocket(this.url);
  173. ws.binaryType = 'blob';
  174. ws.onopen = (ev) => {
  175. this.reconnectTimes = 0;
  176. readyStateCallback.onOpen(ev);
  177. };
  178. ws.onclose = (ev) => {
  179. this.reconnect();
  180. readyStateCallback.onClose(ev);
  181. };
  182. ws.onerror = (ev) => {
  183. this.reconnect();
  184. readyStateCallback.onError(ev);
  185. };
  186. ws.onmessage = (ev) => {
  187. if (ev.data instanceof Blob) {
  188. let reader = new FileReader();
  189. reader.readAsArrayBuffer(ev.data);
  190. reader.onload = () => {
  191. try {
  192. let packet = new Packet().unPack(reader.result);
  193. let packetLength = packet.headerLength + packet.bodyLength + 20;
  194. if (packetLength > this.maxPayload) {
  195. throw new Error('the packet is big than ' + this.maxPayload);
  196. }
  197. let operator = Number(packet.operator) + Number(packet.sequence);
  198. if (this.listeners.has(operator)) {
  199. if (packet.body === '') {
  200. packet.body = '{}';
  201. }
  202. (<(data: string) => void>this.listeners.get(operator))(
  203. packet.body,
  204. );
  205. }
  206. if (operator !== 0 && packet.body !== 'null') {
  207. console.info('receive data', packet.body);
  208. }
  209. } catch (e) {
  210. throw new Error(e);
  211. }
  212. };
  213. } else {
  214. throw new Error('unsupported data format');
  215. }
  216. };
  217. return ws;
  218. }
  219. reconnect() {
  220. if (!this.reconnectLock) {
  221. this.reconnectLock = true;
  222. console.info('websocket reconnect in ' + this.reconnectTimes + 's');
  223. // 尝试重连
  224. setTimeout(() => {
  225. this.reconnectTimes++;
  226. this.socket = this.connect();
  227. this.reconnectLock = false;
  228. }, this.reconnectTimes * 1000);
  229. }
  230. }
  231. }
  232. export { Client, MAX_PAYLOAD };