WebsocketClient.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import { ApiReturn, TsrpcError, WsClient } from "tsrpc-browser";
  2. import { ServiceType } from "../shared/protocols/serviceProto_public";
  3. import { NetUtil } from "./NetUtil";
  4. import { MsgPong } from "../shared/protocols/public/MsgPong";
  5. export class WebsocketClient {
  6. public static EVENT_DISCONNECTED = 'WebsocketClient.EVENT_DISCONNECTED';
  7. protected _conn: WsClient<ServiceType> = null;
  8. protected _serverUrl = '';
  9. protected _eventHandlers: { [key: string]: { event: string, func: Function, thisArg: any }[] } = {};
  10. protected _handlers: { msg: string, func: Function, handler: Function, thisArg: any }[] = [];
  11. public get conn(): WsClient<ServiceType> {
  12. return this._conn;
  13. }
  14. private _latencyCache = [];
  15. private _sortedLatencyCache = [];
  16. private _beginLatencyIndex = 0;
  17. // latency in ms
  18. protected _latency = 0;
  19. public get latency() {
  20. return this._latency;
  21. }
  22. private _lastDisconnectReason: { code?: number, reason?: string } | undefined;
  23. public get lastDisconnectReason() {
  24. return this._lastDisconnectReason;
  25. }
  26. public get type() {
  27. return 'websocket';
  28. }
  29. constructor() {
  30. this.listenMsg("Pong", (msg: MsgPong) => {
  31. let len = this._latencyCache.length;
  32. let latency = Math.ceil((Date.now() - msg.timestamp) / 2);
  33. if (this._latencyCache.length == 12) {
  34. this._latencyCache[this._beginLatencyIndex] = latency;
  35. this._beginLatencyIndex = (this._beginLatencyIndex++) % 12;
  36. }
  37. else {
  38. this._latencyCache.push(latency);
  39. }
  40. if (len < 3) {
  41. this._latencyCache.push(latency);
  42. this._latency = latency;
  43. }
  44. else {
  45. let totalLatency = 0;
  46. this._sortedLatencyCache.length = 0;
  47. this._sortedLatencyCache.push(...this._latencyCache);
  48. this._sortedLatencyCache.sort((a, b) => a - b);
  49. //去除头尾(最高和最低,消除振荡)
  50. for (let i = 1; i < this._sortedLatencyCache.length - 1; ++i) {
  51. totalLatency += this._sortedLatencyCache[i];
  52. }
  53. this._latency = Math.ceil(totalLatency / (this._sortedLatencyCache.length - 2));
  54. }
  55. });
  56. }
  57. get isConnected() {
  58. if (!this._conn) {
  59. return false;
  60. }
  61. return this._conn.isConnected;
  62. }
  63. createConnection(serverUrls: Readonly<string[]>) {
  64. if (this._conn) {
  65. this._conn.disconnect(1000, 'switch_server');
  66. }
  67. let index = Math.floor(serverUrls.length * Math.random());
  68. let serverUrl = serverUrls[index];
  69. this._serverUrl = serverUrl;
  70. this._conn = NetUtil.createWebsocketClient(this._serverUrl);
  71. this._conn.flows.postDisconnectFlow.push(v => {
  72. this._lastDisconnectReason = v;
  73. //this._dispatchEvent(WebsocketClient.EVENT_DISCONNECTED, [v,this]);
  74. return v;
  75. });
  76. /*
  77. //for message debug.
  78. this._conn.flows.preRecvMsgFlow.push( v => {
  79. if(v.msgName.indexOf("game/") != 0){
  80. console.log(v);
  81. }
  82. return v;
  83. });
  84. */
  85. for (let i = 0; i < this._handlers.length; ++i) {
  86. this._conn.listenMsg(this._handlers[i].msg as any, this._handlers[i].handler as any);
  87. }
  88. this.startPing();
  89. }
  90. on(event: string, func: Function, thisArg?: any) {
  91. let handler = func;
  92. if (thisArg) {
  93. handler = func.bind(thisArg);
  94. }
  95. let handlers = this._eventHandlers[event] || [];
  96. handlers.push({ event: event, func: func, thisArg: thisArg });
  97. this._eventHandlers[event] = handlers;
  98. }
  99. off(event: string, func?: Function, thisArg?: any) {
  100. let handlers = this._eventHandlers[event];
  101. if (handlers) {
  102. for (let i = 0; i < handlers.length; ++i) {
  103. let item = handlers[i];
  104. if (item.func === func && item.thisArg == thisArg) {
  105. handlers.splice(i, 1);
  106. return;
  107. }
  108. }
  109. }
  110. }
  111. private _dispatchEvent(event: string, args) {
  112. let handlers = this._eventHandlers[event];
  113. if (handlers) {
  114. for (let i = 0; i < handlers.length; ++i) {
  115. let item = handlers[i];
  116. item.func.apply(item.thisArg, args);
  117. }
  118. }
  119. }
  120. private _internal = -1;
  121. startPing() {
  122. clearInterval(this._internal);
  123. this._internal = setInterval(() => {
  124. if (this._conn.isConnected) {
  125. //console.log('ping');
  126. this.sendMsg('Ping', { timestamp: Date.now() });
  127. }
  128. }, 5000);
  129. }
  130. public get serverUrl(): string {
  131. return this._serverUrl;
  132. }
  133. public listenMsg<T extends keyof ServiceType['msg']>(msgName: T | RegExp, func: Function, thisArg?: any) {
  134. let handler = func;
  135. if (thisArg) {
  136. handler = func.bind(thisArg);
  137. }
  138. this._handlers.push({ msg: msgName as string, func: func, handler: handler, thisArg: thisArg });
  139. this._conn?.listenMsg(msgName, handler as any);
  140. }
  141. public unlistenMsg<T extends keyof ServiceType['msg']>(msgName: T | RegExp, func: Function, thisArg?: any) {
  142. for (let i = 0; i < this._handlers.length; ++i) {
  143. let item = this._handlers[i];
  144. if (item.msg === msgName && item.func === func || item.thisArg == thisArg) {
  145. this._handlers.splice(i, 1);
  146. this._conn?.unlistenMsg(msgName, item.handler);
  147. return;
  148. }
  149. }
  150. }
  151. public unlistenMsgAll<T extends keyof ServiceType['msg']>(msgName: T | RegExp): void {
  152. for (let i = this._handlers.length - 1; i >= 0; --i) {
  153. let item = this._handlers[i];
  154. if (item.msg === msgName) {
  155. this._handlers.splice(i, 1);
  156. }
  157. }
  158. this._conn?.unlistenMsgAll(msgName);
  159. }
  160. public callApi<T extends string & keyof ServiceType['api']>(apiName: T, req: ServiceType['api'][T]['req'], options?: any): Promise<ApiReturn<ServiceType['api'][T]['res']>> {
  161. return this._conn?.callApi(apiName, req, options);
  162. }
  163. public sendMsg<T extends string & keyof ServiceType['msg']>(msgName: T, msg: ServiceType['msg'][T], options?: { timeout?: number, abortKey?: string }): Promise<{
  164. isSucc: true;
  165. } | {
  166. isSucc: false;
  167. err: TsrpcError;
  168. }> {
  169. if (this._conn?.isConnected) {
  170. return this._conn?.sendMsg(msgName, msg, options);
  171. }
  172. }
  173. public async ensureConnected() {
  174. let ret = await this._connect();
  175. return ret;
  176. }
  177. disconnect(code?: number, reason?: string) {
  178. this._lastDisconnectReason = { code, reason };
  179. this._conn?.disconnect(code, reason);
  180. clearInterval(this._internal);
  181. this._internal = - 1;
  182. }
  183. private async _connect(): Promise<{ isSucc: boolean, err?: { code?: string, message: string } }> {
  184. // Connect
  185. let resConnect = await this._conn?.connect();
  186. if (!resConnect.isSucc) {
  187. return { isSucc: false, err: { message: resConnect.errMsg } };
  188. }
  189. return { isSucc: true };
  190. }
  191. }