|
|
@@ -1,278 +1,378 @@
|
|
|
import { v4 } from 'uuid'
|
|
|
-import { Message, Options, ReadyState, RequestCode, MessageType, AsyncTask } from './types'
|
|
|
+import { Package40, Package50 } from '../package';
|
|
|
+import { Package, ConnectionState, SendMessage, AsyncTask } from './types';
|
|
|
+import moment from 'moment'
|
|
|
|
|
|
-export default class {
|
|
|
- readonly host;
|
|
|
+/**
|
|
|
+ * MTP2.0 长链通信类
|
|
|
+ */
|
|
|
+export class MTP2WebSocket<T extends Package40 | Package50>{
|
|
|
+ /** 报文协议 */
|
|
|
+ private readonly Package: Package<T>;
|
|
|
/** 当前实例标识 */
|
|
|
private uuid = '';
|
|
|
+ /** 服务端地址 */
|
|
|
+ private host;
|
|
|
/** WebSocket 对象 */
|
|
|
private ws?: WebSocket;
|
|
|
- /** WebSocket 准备完成 */
|
|
|
- private wsReady?: Promise<WebSocket>;
|
|
|
- /** WebSocket 子协议 */
|
|
|
- private protocols?: string | string[];
|
|
|
-
|
|
|
- /** 信息发送异步建值对 */
|
|
|
- private asyncTaskMap = new Map<string, AsyncTask>();
|
|
|
-
|
|
|
- /** 超时定时器 */
|
|
|
- private timeoutTimer = 0;
|
|
|
- /** 消息回复超时时间 */
|
|
|
- private timeout = 1000 * 15;
|
|
|
-
|
|
|
/** 心跳定时器 */
|
|
|
- private heartbeatTimer = 0;
|
|
|
- /** 心跳间隔时长 */
|
|
|
- private heartbeatInterval = 1000 * 30;
|
|
|
-
|
|
|
+ private beatTimer = 0;
|
|
|
+ /** 心跳发送时间 */
|
|
|
+ private beatTimerId = '';
|
|
|
/** 重连定时器 */
|
|
|
private reconnectTimer = 0;
|
|
|
- /** 本次重连次数 */
|
|
|
- private reconnectCount = 0;
|
|
|
- /** 限制重连次数,0 = 无限制 */
|
|
|
- private reconnectLimit = 10;
|
|
|
-
|
|
|
- /** 是否启用心跳检测 */
|
|
|
- heartbeat;
|
|
|
- /** WebSocket 连接状态 */
|
|
|
- readyState = ReadyState.Closed;
|
|
|
+ /** 超时定时器 */
|
|
|
+ private timeoutTimer = 0;
|
|
|
+ /** 当前流水号 */
|
|
|
+ private currentSerial = 1;
|
|
|
+ /** 信息发送异步建值对 */
|
|
|
+ private asyncTaskMap = new Map<number, AsyncTask<T>>();
|
|
|
+ /** 连接准备完成 */
|
|
|
+ private onReady?: Promise<MTP2WebSocket<T>>;
|
|
|
|
|
|
/** 连接成功的回调 */
|
|
|
- onOpen?: () => void;
|
|
|
+ onOpen?: (socket: MTP2WebSocket<T>) => void;
|
|
|
/** 连接断开的回调 */
|
|
|
onClose?: () => void;
|
|
|
/** 连接发生错误的回调 */
|
|
|
- onError?: (err: Event) => void;
|
|
|
- /** 接收推送消息的回调 */
|
|
|
- onPush?: (message: { requestCode: RequestCode; data: unknown; }) => void;
|
|
|
+ onError?: (e: Event) => void;
|
|
|
+ /** 接收到推送类报文的回调 */
|
|
|
+ onPush?: (data: T) => void;
|
|
|
/** 在重连之前回调 */
|
|
|
onBeforeReconnect?: (count: number) => void;
|
|
|
/** 在重连成功之后回调 */
|
|
|
onReconnect?: () => void;
|
|
|
|
|
|
- constructor(host: string, options?: Options) {
|
|
|
- this.host = host
|
|
|
- if (options) {
|
|
|
- this.heartbeat = options.heartbeat
|
|
|
- this.protocols = options.protocols
|
|
|
- }
|
|
|
- if (!options?.manual) {
|
|
|
- this.connect()
|
|
|
- }
|
|
|
+ /** 当前连接状态 */
|
|
|
+ connState: keyof typeof ConnectionState = 'Unconnected';
|
|
|
+ /** 重连次数 */
|
|
|
+ reconnectCount = 0;
|
|
|
+ /** 心跳间隔时长,默认为30秒 */
|
|
|
+ beatInterval = 30 * 1000;
|
|
|
+ /** 消息超时时长,默认为15秒 */
|
|
|
+ timeoutInterval = 15 * 1000;
|
|
|
+
|
|
|
+ constructor(pkg: Package<T>, host = '') {
|
|
|
+ this.Package = pkg;
|
|
|
+ this.host = host;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 连接服务器
|
|
|
*/
|
|
|
- connect() {
|
|
|
- if (!this.wsReady || this.readyState === ReadyState.Closed) {
|
|
|
- clearTimeout(this.reconnectTimer)
|
|
|
- this.stopHeartbeat()
|
|
|
- this.readyState = ReadyState.Connecting
|
|
|
- console.log(this.host, '正在连接')
|
|
|
+ async connect(host?: string, protocols?: string | string[]): Promise<MTP2WebSocket<T>> {
|
|
|
+ if (!this.onReady) {
|
|
|
+ clearTimeout(this.reconnectTimer);
|
|
|
+ this.stopHeartBeat();
|
|
|
+ this.host = host || this.host;
|
|
|
+ this.connState = 'Connecting';
|
|
|
+ console.log(this.host, '正在连接');
|
|
|
|
|
|
- this.wsReady = new Promise((resolve, reject) => {
|
|
|
- try {
|
|
|
- const ws = new WebSocket(this.host, this.protocols)
|
|
|
- const uuid = v4()
|
|
|
- this.uuid = uuid
|
|
|
+ this.onReady = new Promise((resolve, reject) => {
|
|
|
+ const errMsg = this.host + ' 连接发生错误';
|
|
|
+ const uuid = v4();
|
|
|
+ this.uuid = uuid;
|
|
|
|
|
|
+ try {
|
|
|
+ this.ws = new WebSocket(this.host, protocols);
|
|
|
// 连接成功
|
|
|
- ws.onopen = () => {
|
|
|
- console.log(this.host, '连接成功')
|
|
|
- this.readyState = ReadyState.Open
|
|
|
- this.startHeartbeat()
|
|
|
- this.onOpen && this.onOpen()
|
|
|
- resolve(ws)
|
|
|
+ this.ws.onopen = () => {
|
|
|
+ console.log(this.host, '连接成功');
|
|
|
+ this.connState = 'Connected';
|
|
|
+ this.startHeartBeat();
|
|
|
+ this.onOpen && this.onOpen(this);
|
|
|
+ resolve(this);
|
|
|
}
|
|
|
// 连接断开(CLOSING有可能延迟)
|
|
|
- ws.onclose = () => {
|
|
|
+ this.ws.onclose = () => {
|
|
|
// 判断是否当前实例
|
|
|
// 如果连接断开后不等待 onclose 响应,由于 onclose 延迟的原因可能会在创建新的 ws 实例后触发,导致刚创建的实例被断开进入重连机制
|
|
|
if (this.uuid === uuid) {
|
|
|
- console.warn(this.host, '连接已断开')
|
|
|
- this.reset()
|
|
|
- if (this.reconnectLimit === 0 || this.reconnectCount < this.reconnectLimit) {
|
|
|
- this.reconnect() // 重连失败会不断尝试,直到成功为止
|
|
|
- } else {
|
|
|
- this.reconnectCount = 0
|
|
|
- }
|
|
|
+ console.warn(this.host, '连接已断开');
|
|
|
+ this.reset();
|
|
|
+ this.reconnect(); // 重连失败会不断尝试,直到成功为止
|
|
|
}
|
|
|
}
|
|
|
// 连接发生错误
|
|
|
- ws.onerror = (e) => {
|
|
|
- console.error('连接发生错误', e)
|
|
|
- this.onError && this.onError(e)
|
|
|
- reject(e)
|
|
|
+ this.ws.onerror = (e) => {
|
|
|
+ this.onError && this.onError(e);
|
|
|
+ reject(errMsg);
|
|
|
}
|
|
|
- // 接收消息
|
|
|
- ws.onmessage = (e) => {
|
|
|
- console.log('收到消息', e.data)
|
|
|
- this.response(e.data)
|
|
|
+ // 接收数据
|
|
|
+ this.ws.onmessage = (e) => {
|
|
|
+ // 接收数据
|
|
|
+ new Response(e.data).arrayBuffer().then((res) => {
|
|
|
+ this.disposeReceiveDatas(new Uint8Array(res));
|
|
|
+ })
|
|
|
}
|
|
|
-
|
|
|
- this.ws = ws
|
|
|
- } catch (e) {
|
|
|
- reject(e)
|
|
|
+ } catch {
|
|
|
+ reject(errMsg);
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
- return this.wsReady
|
|
|
+ return this.onReady
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 重置连接状态
|
|
|
+ * 主动断开连接,断开后不会自动重连
|
|
|
*/
|
|
|
- private reset() {
|
|
|
- this.ws = undefined
|
|
|
- this.wsReady = undefined
|
|
|
- this.readyState = ReadyState.Closed
|
|
|
- this.asyncTaskMap.clear()
|
|
|
- this.onClose && this.onClose()
|
|
|
+ close() {
|
|
|
+ clearTimeout(this.reconnectTimer);
|
|
|
+ this.stopHeartBeat();
|
|
|
+ this.uuid = v4(); // 改变实例标识,取消重连状态
|
|
|
+ this.reconnectCount = 0;
|
|
|
+
|
|
|
+ if (this.ws) {
|
|
|
+ this.ws.close();
|
|
|
+ console.warn(this.host, '主动断开');
|
|
|
+ }
|
|
|
+
|
|
|
+ this.reset(); // 不等待 ws.onclose 响应强制重置实例
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 主动断开连接,断开后不会自动重连
|
|
|
- * @param forced 是否强制断开
|
|
|
+ * 重置实例
|
|
|
*/
|
|
|
- close(forced = false) {
|
|
|
- return new Promise<void>((resolve) => {
|
|
|
- clearTimeout(this.reconnectTimer)
|
|
|
- this.stopHeartbeat()
|
|
|
- this.uuid = v4() // 改变实例标识,取消重连状态
|
|
|
- this.reconnectCount = 0
|
|
|
+ private reset() {
|
|
|
+ this.ws = undefined;
|
|
|
+ this.onReady = undefined;
|
|
|
+ this.connState = 'Unconnected';
|
|
|
+ this.onClose && this.onClose();
|
|
|
+ }
|
|
|
|
|
|
- if (!forced && this.ws) {
|
|
|
- const listener = () => {
|
|
|
- console.warn(this.host, '主动断开')
|
|
|
- this.ws?.removeEventListener('close', listener)
|
|
|
- this.reset()
|
|
|
- resolve()
|
|
|
+ /**
|
|
|
+ * 启动心跳请求包发送Timer
|
|
|
+ */
|
|
|
+ private startHeartBeat() {
|
|
|
+ this.beatTimer = window.setTimeout(() => {
|
|
|
+ this.beatTimerId = moment(new Date()).format('HH:mm:ss');
|
|
|
+ console.log(this.host, '发送心跳', this.beatTimerId);
|
|
|
+ // 发送心跳
|
|
|
+ switch (this.Package) {
|
|
|
+ case Package40: {
|
|
|
+ // 4.0
|
|
|
+ this.send({
|
|
|
+ data: { rspCode: -1, payload: new this.Package(0x12) }
|
|
|
+ })
|
|
|
+ break;
|
|
|
}
|
|
|
- this.ws.addEventListener('close', listener)
|
|
|
- this.ws.close()
|
|
|
- } else {
|
|
|
- if (this.ws) {
|
|
|
- console.warn(this.host, '主动断开')
|
|
|
- this.ws.close()
|
|
|
+ case Package50: {
|
|
|
+ // 5.0
|
|
|
+ this.send({
|
|
|
+ data: { rspCode: 0, payload: new this.Package(0) }
|
|
|
+ })
|
|
|
+ break;
|
|
|
}
|
|
|
- this.reset()
|
|
|
- resolve()
|
|
|
}
|
|
|
- })
|
|
|
+ // 如果已经超过或心跳超时时长没有收到心跳回复,则认为网络已经异常,进行断网重连
|
|
|
+ // WARNING: - 注意在正常断网时停止心跳逻辑
|
|
|
+ this.timeoutTimer = window.setTimeout(() => {
|
|
|
+ console.warn(this.host, '心跳超时', this.beatTimerId);
|
|
|
+ this.reconnect();
|
|
|
+ }, this.timeoutInterval)
|
|
|
+ }, this.beatInterval)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 发送消息
|
|
|
- * @param message
|
|
|
+ * 停止心跳请求包发送Timer
|
|
|
*/
|
|
|
- async send<T>(params: Message) {
|
|
|
- const ws = await this.connect()
|
|
|
- return new Promise<T>((resolve, reject) => {
|
|
|
- const { requestId } = params.headers
|
|
|
+ private stopHeartBeat() {
|
|
|
+ clearTimeout(this.timeoutTimer);
|
|
|
+ clearTimeout(this.beatTimer);
|
|
|
+ }
|
|
|
|
|
|
- // 消息超时回调
|
|
|
- const timeoutId = window.setTimeout(() => {
|
|
|
- reject('业务超时')
|
|
|
- this.asyncTaskMap.delete(requestId)
|
|
|
- }, this.timeout)
|
|
|
+ /**
|
|
|
+ * 断网重连方法,在重连尝试失败时会再次重试
|
|
|
+ */
|
|
|
+ private reconnect() {
|
|
|
+ this.stopHeartBeat();
|
|
|
+ if (this.connState !== 'Connecting') {
|
|
|
+ this.reconnectCount++;
|
|
|
+ this.onBeforeReconnect && this.onBeforeReconnect(this.reconnectCount);
|
|
|
|
|
|
- this.asyncTaskMap.set(requestId, {
|
|
|
- timeoutId,
|
|
|
- callback: (data) => resolve(data as T),
|
|
|
- })
|
|
|
+ // 自动计算每次重试的延时,重试次数越多,延时越大
|
|
|
+ const delay = this.reconnectCount * 5000
|
|
|
+ console.log(this.host, `${delay / 1000}秒后将进行第${this.reconnectCount}次重连`);
|
|
|
|
|
|
- ws.send(JSON.stringify(params))
|
|
|
- })
|
|
|
+ this.reconnectTimer = window.setTimeout(() => {
|
|
|
+ this.onReady = undefined;
|
|
|
+ this.connect().then(() => {
|
|
|
+ console.log(this.host, '重连成功,可开始进行业务操作');
|
|
|
+ this.reconnectCount = 0;
|
|
|
+ this.onReconnect && this.onReconnect();
|
|
|
+ }).catch(() => {
|
|
|
+ // 重连失败会进入 ws.onclose 再次发起重连
|
|
|
+ if (this.reconnectCount) {
|
|
|
+ console.warn(this.host, `第${this.reconnectCount}次重连失败`);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }, delay);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 响应消息
|
|
|
- * @param data
|
|
|
+ * 发送报文
|
|
|
+ * @param data 目标报文
|
|
|
+ * @param fncode 回复号。P为Package40对象时为回复大类号,同时必传;P为Package50对象为回复功能码,可不传。
|
|
|
+ * @param success 成功回调
|
|
|
+ * @param fail 失败回调
|
|
|
*/
|
|
|
- private response(message: string) {
|
|
|
- const { headers, data }: Message = JSON.parse(message)
|
|
|
- const asyncTask = this.asyncTaskMap.get(headers.requestId)
|
|
|
+ send(msg: SendMessage<T>) {
|
|
|
+ const { data, success, fail } = msg;
|
|
|
+ const { payload } = data;
|
|
|
|
|
|
- switch (headers.messageType) {
|
|
|
- case MessageType.Heartbeat:
|
|
|
- this.stopHeartbeat()
|
|
|
- this.startHeartbeat()
|
|
|
- break
|
|
|
- case MessageType.Request:
|
|
|
- asyncTask?.callback && asyncTask.callback(data)
|
|
|
- break
|
|
|
- case MessageType.Push:
|
|
|
- this.onPush && this.onPush({
|
|
|
- requestCode: headers.requestCode,
|
|
|
- data
|
|
|
- })
|
|
|
- break
|
|
|
- }
|
|
|
+ if (this.onReady) {
|
|
|
+ this.onReady.then(() => {
|
|
|
+ const mapKey = this.currentSerial;
|
|
|
+ payload.serialNumber = mapKey; // 设置流水号
|
|
|
|
|
|
- if (asyncTask) {
|
|
|
- clearTimeout(asyncTask.timeoutId)
|
|
|
- this.asyncTaskMap.delete(headers.requestId)
|
|
|
+ // 判断是否需要异步回调
|
|
|
+ if (success || fail) {
|
|
|
+ // 保存发送任务,为任务设置一个自动超时定时器
|
|
|
+ this.asyncTaskMap.set(mapKey, {
|
|
|
+ sendMessage: msg,
|
|
|
+ timeoutId: window.setTimeout(() => this.asyncTimeout(mapKey), this.timeoutInterval),
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 发送信息
|
|
|
+ this.ws?.send(payload.data());
|
|
|
+ this.currentSerial++;
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ fail && fail('服务未连接');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 发送心跳检测
|
|
|
+ /**
|
|
|
+ * 异步任务超时
|
|
|
+ * @param key key
|
|
|
*/
|
|
|
- private startHeartbeat() {
|
|
|
- if (this.heartbeat) {
|
|
|
- this.heartbeatTimer = window.setTimeout(() => {
|
|
|
- this.send({
|
|
|
- headers: {
|
|
|
- requestId: v4(),
|
|
|
- requestCode: RequestCode.Heartbeat,
|
|
|
- messageType: MessageType.Heartbeat,
|
|
|
- timestamp: new Date().getTime()
|
|
|
- },
|
|
|
- data: '心跳检测'
|
|
|
- })
|
|
|
- // 没有收到心跳回复,则认为网络已经异常,进行断网重连
|
|
|
- this.timeoutTimer = window.setTimeout(() => {
|
|
|
- console.warn(this.host, '心跳超时')
|
|
|
- this.reconnect()
|
|
|
- }, this.timeout)
|
|
|
- }, this.heartbeatInterval)
|
|
|
- }
|
|
|
+ private asyncTimeout(key: number) {
|
|
|
+ // 获取对应异步任务对象
|
|
|
+ const asyncTask = this.asyncTaskMap.get(key);
|
|
|
+ const onFail = asyncTask?.sendMessage.fail;
|
|
|
+ onFail && onFail('业务超时');
|
|
|
+ this.asyncTaskMap.delete(key);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 停止心跳检测
|
|
|
+ /**
|
|
|
+ * 处理接收数据
|
|
|
+ * @param bytes 接收的数据
|
|
|
*/
|
|
|
- private stopHeartbeat() {
|
|
|
- clearTimeout(this.timeoutTimer)
|
|
|
- clearTimeout(this.heartbeatTimer)
|
|
|
+ private disposeReceiveDatas(bytes: Uint8Array) {
|
|
|
+ const cache: number[] = [];
|
|
|
+ let cachePackageLength = 0;
|
|
|
+
|
|
|
+ for (let i = 0; i < bytes.length; i++) {
|
|
|
+ const byte = bytes[i];
|
|
|
+ cache.push(byte);
|
|
|
+
|
|
|
+ if (i === 0 && byte !== 0xff) {
|
|
|
+ console.error('接收到首字节不是0xFF');
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ // 取报文长度
|
|
|
+ if (cache.length === 5) {
|
|
|
+ switch (this.Package) {
|
|
|
+ // 4.0报文
|
|
|
+ case Package40: {
|
|
|
+ cachePackageLength = new DataView(new Uint8Array(new Uint8Array(cache).subarray(1, 5)).buffer).getUint32(0, false);
|
|
|
+ if (cachePackageLength > 65535) {
|
|
|
+ console.error('接收到长度超过65535的行情包');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // 5.0报文
|
|
|
+ case Package50: {
|
|
|
+ const sub = new Uint8Array(cache).subarray(1, 5);
|
|
|
+ const dataView = new DataView(new Uint8Array(sub).buffer); // 注意这里要new一个新的Uint8Array,直接buffer取到的还是原数组的buffer
|
|
|
+ cachePackageLength = dataView.getUint32(0, true);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 判断是否已经到包尾
|
|
|
+ if (cache.length === cachePackageLength) {
|
|
|
+ if (byte !== 0x0) {
|
|
|
+ console.error('接收到尾字节不是0x0的错误数据包');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const content = new Uint8Array(cache);
|
|
|
+ const result = new this.Package(content);
|
|
|
+
|
|
|
+ if (result.packageLength === 0) {
|
|
|
+ console.error('报文装箱失败');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.disposePackageResult(result);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 断开重连,在重连尝试失败时会再次重试
|
|
|
+ * 处理完整的报文
|
|
|
*/
|
|
|
- private reconnect() {
|
|
|
- this.stopHeartbeat() // 重连之前停止心跳检测
|
|
|
- if (this.readyState !== ReadyState.Connecting) {
|
|
|
- this.reconnectCount++
|
|
|
- this.onBeforeReconnect && this.onBeforeReconnect(this.reconnectCount)
|
|
|
+ private disposePackageResult(p: T) {
|
|
|
+ const asyncTask = this.asyncTaskMap.get(p.serialNumber);
|
|
|
+ const sendMessage = asyncTask?.sendMessage;
|
|
|
|
|
|
- // 自动计算每次重试的延时,重试次数越多,延时越大
|
|
|
- const delay = this.reconnectCount * 3000
|
|
|
- console.log(this.host, `${delay / 1000}秒后将进行第${this.reconnectCount}次重连`)
|
|
|
+ // 4.0报文
|
|
|
+ if (p instanceof Package40) {
|
|
|
+ // 推送类报文, 0x12 - 心跳, 0x41 - 实时行情推送, 0x42 - 控制信号
|
|
|
+ switch (p.mainClassNumber) {
|
|
|
+ case 0x12: {
|
|
|
+ // 接收到心跳回复
|
|
|
+ console.log(this.host, '收到心跳回复', this.beatTimerId);
|
|
|
+ this.stopHeartBeat();
|
|
|
+ this.startHeartBeat();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case 0x41:
|
|
|
+ case 0x42: {
|
|
|
+ // 推送类报文
|
|
|
+ this.onPush && this.onPush(p);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default: {
|
|
|
+ // 非推送类报文
|
|
|
+ sendMessage?.success && sendMessage.success(p);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- this.reconnectTimer = window.setTimeout(() => {
|
|
|
- this.connect().then(() => {
|
|
|
- console.log(this.host, '重连成功,可开始进行业务操作')
|
|
|
- this.reconnectCount = 0
|
|
|
- this.onReconnect && this.onReconnect()
|
|
|
- }).catch(() => {
|
|
|
- // 重连失败会进入 ws.onclose 再次发起重连
|
|
|
- if (this.reconnectCount) {
|
|
|
- console.warn(this.host, `第${this.reconnectCount}次重连失败`)
|
|
|
- }
|
|
|
- })
|
|
|
- }, delay)
|
|
|
+ // 5.0报文
|
|
|
+ if (p instanceof Package50) {
|
|
|
+ if (p.funCode === 0) {
|
|
|
+ // 接收到心跳回复
|
|
|
+ console.log(this.host, '收到心跳回复', this.beatTimerId);
|
|
|
+ this.stopHeartBeat();
|
|
|
+ this.startHeartBeat();
|
|
|
+ } else if (p.serialNumber === 0) {
|
|
|
+ // 推送类报文
|
|
|
+ this.onPush && this.onPush(p);
|
|
|
+ } else if (sendMessage) {
|
|
|
+ // 非推送类报文
|
|
|
+ const { data, success } = sendMessage;
|
|
|
+
|
|
|
+ // 可能会收到多个流水号相同的数据包,需判断是否正确的回复码
|
|
|
+ if (data.rspCode === p.funCode) {
|
|
|
+ success && success(p);
|
|
|
+ } else {
|
|
|
+ // 跳过当前收到的数据包,继续等待直到asyncTask超时
|
|
|
+ //console.log('无效的回复码', p.funCode)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 移除当前发送的任务请求
|
|
|
+ if (asyncTask) {
|
|
|
+ clearTimeout(asyncTask.timeoutId);
|
|
|
+ this.asyncTaskMap.delete(p.serialNumber);
|
|
|
}
|
|
|
}
|
|
|
}
|