@pixelhue/event-controller-sdk 0.0.1 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/packages/tcp/ConnectManager.d.ts +1 -2
- package/dist/packages/tcp/ConnectManager.js +2 -3
- package/dist/packages/tcp/QueueManager.d.ts +0 -1
- package/dist/packages/tcp/QueueManager.js +9 -14
- package/dist/packages/tcp/SocketWrapper.d.ts +13 -3
- package/dist/packages/tcp/SocketWrapper.js +68 -28
- package/dist/packages/tcp/constants.d.ts +0 -1
- package/dist/packages/tcp/constants.js +0 -1
- package/dist/packages/tcp/discoveryService.d.ts +0 -1
- package/dist/packages/tcp/discoveryService.js +0 -1
- package/dist/packages/tcp/protocolHandler.d.ts +0 -1
- package/dist/packages/tcp/protocolHandler.js +2 -2
- package/dist/packages/tcp/tcpWrapper.d.ts +27 -4
- package/dist/packages/tcp/tcpWrapper.js +106 -50
- package/dist/packages/tcp/types.d.ts +0 -1
- package/dist/packages/tcp/types.js +0 -1
- package/dist/packages/tcp/utils.d.ts +0 -1
- package/dist/packages/tcp/utils.js +0 -1
- package/package.json +87 -82
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/packages/tcp/ConnectManager.d.ts.map +0 -1
- package/dist/packages/tcp/ConnectManager.js.map +0 -1
- package/dist/packages/tcp/QueueManager.d.ts.map +0 -1
- package/dist/packages/tcp/QueueManager.js.map +0 -1
- package/dist/packages/tcp/SocketWrapper.d.ts.map +0 -1
- package/dist/packages/tcp/SocketWrapper.js.map +0 -1
- package/dist/packages/tcp/constants.d.ts.map +0 -1
- package/dist/packages/tcp/constants.js.map +0 -1
- package/dist/packages/tcp/demo.d.ts +0 -3
- package/dist/packages/tcp/demo.d.ts.map +0 -1
- package/dist/packages/tcp/demo.js +0 -164
- package/dist/packages/tcp/demo.js.map +0 -1
- package/dist/packages/tcp/discoveryService.d.ts.map +0 -1
- package/dist/packages/tcp/discoveryService.js.map +0 -1
- package/dist/packages/tcp/protocolHandler.d.ts.map +0 -1
- package/dist/packages/tcp/protocolHandler.js.map +0 -1
- package/dist/packages/tcp/tcpWrapper.d.ts.map +0 -1
- package/dist/packages/tcp/tcpWrapper.js.map +0 -1
- package/dist/packages/tcp/test-build-parse-debug.d.ts +0 -2
- package/dist/packages/tcp/test-build-parse-debug.d.ts.map +0 -1
- package/dist/packages/tcp/test-build-parse-debug.js +0 -73
- package/dist/packages/tcp/test-build-parse-debug.js.map +0 -1
- package/dist/packages/tcp/test-packet-split.d.ts +0 -43
- package/dist/packages/tcp/test-packet-split.d.ts.map +0 -1
- package/dist/packages/tcp/test-packet-split.js +0 -417
- package/dist/packages/tcp/test-packet-split.js.map +0 -1
- package/dist/packages/tcp/test-protocol-handler.d.ts +0 -6
- package/dist/packages/tcp/test-protocol-handler.d.ts.map +0 -1
- package/dist/packages/tcp/test-protocol-handler.js +0 -225
- package/dist/packages/tcp/test-protocol-handler.js.map +0 -1
- package/dist/packages/tcp/test-server.d.ts +0 -54
- package/dist/packages/tcp/test-server.d.ts.map +0 -1
- package/dist/packages/tcp/test-server.js +0 -412
- package/dist/packages/tcp/test-server.js.map +0 -1
- package/dist/packages/tcp/types.d.ts.map +0 -1
- package/dist/packages/tcp/types.js.map +0 -1
- package/dist/packages/tcp/utils.d.ts.map +0 -1
- package/dist/packages/tcp/utils.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -14,4 +14,3 @@ export { QueueOverflowStrategy } from './packages/tcp/types.js';
|
|
|
14
14
|
export { SUPPORTED_MODELS, DEFAULT_TCP_PORT, DEFAULT_ENABLE_HEARTBEAT, DEFAULT_AUTO_RECONNECT, DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_RECONNECT_INTERVAL, DEFAULT_HEARTBEAT_TIMEOUT, DEFAULT_MAX_RECONNECT_INTERVAL, DEFAULT_ENABLE_EXPONENTIAL_BACKOFF, DEFAULT_MDNS_QUERY_INTERVAL, DEFAULT_MAX_RECONNECT_ATTEMPTS, } from './packages/tcp/constants.js';
|
|
15
15
|
export { parseHost, parsePort } from './packages/tcp/utils.js';
|
|
16
16
|
export type { ProtocolParseResult } from './packages/tcp/protocolHandler.js';
|
|
17
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -15,4 +15,3 @@ export { QueueOverflowStrategy } from './packages/tcp/types.js';
|
|
|
15
15
|
export { SUPPORTED_MODELS, DEFAULT_TCP_PORT, DEFAULT_ENABLE_HEARTBEAT, DEFAULT_AUTO_RECONNECT, DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_RECONNECT_INTERVAL, DEFAULT_HEARTBEAT_TIMEOUT, DEFAULT_MAX_RECONNECT_INTERVAL, DEFAULT_ENABLE_EXPONENTIAL_BACKOFF, DEFAULT_MDNS_QUERY_INTERVAL, DEFAULT_MAX_RECONNECT_ATTEMPTS, } from './packages/tcp/constants.js';
|
|
16
16
|
// 工具函数导出
|
|
17
17
|
export { parseHost, parsePort } from './packages/tcp/utils.js';
|
|
18
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'eventemitter3';
|
|
2
|
-
import type { ConnectionManagerEvents, SocketConnectOptions } from './types.js';
|
|
3
2
|
import { SocketWrapper } from './SocketWrapper.js';
|
|
3
|
+
import type { ConnectionManagerEvents, SocketConnectOptions } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* MINI连接管理器
|
|
6
6
|
*/
|
|
@@ -63,4 +63,3 @@ export declare class MiniConnectionManager extends EventEmitter<ConnectionManage
|
|
|
63
63
|
*/
|
|
64
64
|
destroy(): Promise<void>;
|
|
65
65
|
}
|
|
66
|
-
//# sourceMappingURL=ConnectManager.d.ts.map
|
|
@@ -71,7 +71,7 @@ export class MiniConnectionManager extends EventEmitter {
|
|
|
71
71
|
const connectionId = this.getConnectionId(address, port);
|
|
72
72
|
const socketWrapper = this.connections.get(connectionId);
|
|
73
73
|
if (socketWrapper) {
|
|
74
|
-
|
|
74
|
+
socketWrapper.disconnect();
|
|
75
75
|
this.connections.delete(connectionId);
|
|
76
76
|
// 触发断开事件,传递 SocketWrapper
|
|
77
77
|
this.emit('disconnected', socketWrapper);
|
|
@@ -169,8 +169,7 @@ export class MiniConnectionManager extends EventEmitter {
|
|
|
169
169
|
*/
|
|
170
170
|
async destroy() {
|
|
171
171
|
this.isShuttingDown = true;
|
|
172
|
-
|
|
172
|
+
this.disconnectAll();
|
|
173
173
|
this.removeAllListeners();
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
|
-
//# sourceMappingURL=ConnectManager.js.map
|
|
@@ -40,18 +40,13 @@ export class QueueManager {
|
|
|
40
40
|
addToQueue(requestId, data, processor, onError, cachedParseResult) {
|
|
41
41
|
this.queue
|
|
42
42
|
.add(async () => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.processedRequestIds.clear();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
throw error; // 重新抛出,让 p-queue 处理
|
|
43
|
+
// 处理队列项
|
|
44
|
+
await processor(requestId, data, cachedParseResult);
|
|
45
|
+
// 去重标记
|
|
46
|
+
this.processedRequestIds.add(requestId);
|
|
47
|
+
// 限制去重集合大小,防止内存泄漏
|
|
48
|
+
if (this.processedRequestIds.size > this.queueOptions.maxLength * 2) {
|
|
49
|
+
this.processedRequestIds.clear();
|
|
55
50
|
}
|
|
56
51
|
})
|
|
57
52
|
.catch((error) => {
|
|
@@ -79,11 +74,12 @@ export class QueueManager {
|
|
|
79
74
|
case QueueOverflowStrategy.DROP_NEWEST:
|
|
80
75
|
// 丢弃最新数据
|
|
81
76
|
break;
|
|
82
|
-
case QueueOverflowStrategy.REJECT:
|
|
77
|
+
case QueueOverflowStrategy.REJECT: {
|
|
83
78
|
// 拒绝新数据并触发错误
|
|
84
79
|
const error = new Error(`Queue overflow: max length ${this.queueOptions.maxLength} reached`);
|
|
85
80
|
onError(error, requestId);
|
|
86
81
|
break;
|
|
82
|
+
}
|
|
87
83
|
case QueueOverflowStrategy.DROP_OLDEST:
|
|
88
84
|
default:
|
|
89
85
|
// p-queue 不支持直接丢弃最旧的任务
|
|
@@ -99,4 +95,3 @@ export class QueueManager {
|
|
|
99
95
|
this.processedRequestIds.clear();
|
|
100
96
|
}
|
|
101
97
|
}
|
|
102
|
-
//# sourceMappingURL=QueueManager.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import type { ISocketWrapper, SocketConnectOptions, QueueOptions, ProtocolSendPayload } from './types.js';
|
|
3
2
|
import { TcpWrapper } from './tcpWrapper.js';
|
|
3
|
+
import type { ISocketWrapper, ProtocolSendPayload, QueueOptions, SocketConnectOptions } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* SocketWrapper - TCP Socket 最小传输封装
|
|
6
6
|
*
|
|
@@ -36,7 +36,7 @@ export declare class SocketWrapper extends EventEmitter implements ISocketWrappe
|
|
|
36
36
|
constructor(queueOptions?: Partial<QueueOptions>);
|
|
37
37
|
/**
|
|
38
38
|
* 检查是否是心跳数据(ping/pong)
|
|
39
|
-
* 使用
|
|
39
|
+
* 使用 TcpWrapper 的 heartbeatResponseChecker 统一检查
|
|
40
40
|
*/
|
|
41
41
|
private isHeartbeatData;
|
|
42
42
|
/**
|
|
@@ -44,6 +44,17 @@ export declare class SocketWrapper extends EventEmitter implements ISocketWrappe
|
|
|
44
44
|
* @param data 接收到的数据
|
|
45
45
|
*/
|
|
46
46
|
private handleReceiveData;
|
|
47
|
+
/**
|
|
48
|
+
* 移除缓冲区开头的心跳数据(处理粘包情况:心跳数据 + 协议数据)
|
|
49
|
+
* @returns 如果移除了心跳数据返回 true,否则返回 false
|
|
50
|
+
*/
|
|
51
|
+
private removeHeartbeatPrefix;
|
|
52
|
+
/**
|
|
53
|
+
* 处理提取出的协议包
|
|
54
|
+
* @param packets 有效协议包列表
|
|
55
|
+
* @param invalidPackets 无效协议包列表
|
|
56
|
+
*/
|
|
57
|
+
private processExtractedPackets;
|
|
47
58
|
/**
|
|
48
59
|
* 处理协议数据(使用半包粘包处理)
|
|
49
60
|
* 使用循环替代递归,避免栈溢出风险
|
|
@@ -103,4 +114,3 @@ export declare class SocketWrapper extends EventEmitter implements ISocketWrappe
|
|
|
103
114
|
*/
|
|
104
115
|
getTcpWrapper(): TcpWrapper;
|
|
105
116
|
}
|
|
106
|
-
//# sourceMappingURL=SocketWrapper.d.ts.map
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
// ALL_CODE_LINE: 329
|
|
2
2
|
// AI_CODE_LINE: 53
|
|
3
3
|
import { EventEmitter } from 'events';
|
|
4
|
-
import { TcpWrapper } from './tcpWrapper.js';
|
|
5
|
-
import { QueueManager, DEFAULT_QUEUE_OPTIONS } from './QueueManager.js';
|
|
6
4
|
import { ProtocolHandler } from './protocolHandler.js';
|
|
5
|
+
import { DEFAULT_QUEUE_OPTIONS, QueueManager } from './QueueManager.js';
|
|
6
|
+
import { TcpWrapper } from './tcpWrapper.js';
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* 心跳数据长度常量
|
|
9
9
|
*/
|
|
10
|
-
const HEARTBEAT_PING = Buffer.from('ping', 'utf8');
|
|
11
|
-
const HEARTBEAT_PONG = Buffer.from('pong', 'utf8');
|
|
12
10
|
const HEARTBEAT_LENGTH = 4;
|
|
13
11
|
/**
|
|
14
12
|
* SocketWrapper - TCP Socket 最小传输封装
|
|
@@ -76,13 +74,11 @@ export class SocketWrapper extends EventEmitter {
|
|
|
76
74
|
}
|
|
77
75
|
/**
|
|
78
76
|
* 检查是否是心跳数据(ping/pong)
|
|
79
|
-
* 使用
|
|
77
|
+
* 使用 TcpWrapper 的 heartbeatResponseChecker 统一检查
|
|
80
78
|
*/
|
|
81
79
|
isHeartbeatData(buffer) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
return buffer.equals(HEARTBEAT_PING) || buffer.equals(HEARTBEAT_PONG);
|
|
80
|
+
const heartbeatResponseChecker = this.tcpWrapper.getHeartbeatResponseChecker();
|
|
81
|
+
return heartbeatResponseChecker(buffer);
|
|
86
82
|
}
|
|
87
83
|
/**
|
|
88
84
|
* 接收数据处理
|
|
@@ -103,6 +99,38 @@ export class SocketWrapper extends EventEmitter {
|
|
|
103
99
|
// 处理协议数据
|
|
104
100
|
this.handleProtocolData();
|
|
105
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* 移除缓冲区开头的心跳数据(处理粘包情况:心跳数据 + 协议数据)
|
|
104
|
+
* @returns 如果移除了心跳数据返回 true,否则返回 false
|
|
105
|
+
*/
|
|
106
|
+
removeHeartbeatPrefix() {
|
|
107
|
+
if (!this.receiveBuffer || this.receiveBuffer.length < HEARTBEAT_LENGTH) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
const first4Bytes = this.receiveBuffer.slice(0, HEARTBEAT_LENGTH);
|
|
111
|
+
if (!this.isHeartbeatData(first4Bytes)) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
// 移除前 4 字节的心跳数据
|
|
115
|
+
this.receiveBuffer =
|
|
116
|
+
this.receiveBuffer.length > HEARTBEAT_LENGTH ? this.receiveBuffer.slice(HEARTBEAT_LENGTH) : null;
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 处理提取出的协议包
|
|
121
|
+
* @param packets 有效协议包列表
|
|
122
|
+
* @param invalidPackets 无效协议包列表
|
|
123
|
+
*/
|
|
124
|
+
processExtractedPackets(packets, invalidPackets) {
|
|
125
|
+
// 处理校验失败的废包
|
|
126
|
+
if (invalidPackets.length > 0) {
|
|
127
|
+
this.emit('error', new Error(`Invalid packets detected: ${invalidPackets.length} packets dropped`));
|
|
128
|
+
}
|
|
129
|
+
// 处理每个有效完整包
|
|
130
|
+
for (const packet of packets) {
|
|
131
|
+
this.processPacket(packet);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
106
134
|
/**
|
|
107
135
|
* 处理协议数据(使用半包粘包处理)
|
|
108
136
|
* 使用循环替代递归,避免栈溢出风险
|
|
@@ -110,34 +138,42 @@ export class SocketWrapper extends EventEmitter {
|
|
|
110
138
|
handleProtocolData() {
|
|
111
139
|
if (!this.receiveBuffer)
|
|
112
140
|
return;
|
|
141
|
+
// 保护机制:跟踪 remaining 长度未变化的次数 防止异常数据导致死循环
|
|
142
|
+
const MAX_STAGNANT_LOOPS = 10; // 最大停滞循环次数
|
|
143
|
+
let stagnantCount = 0;
|
|
144
|
+
let lastRemainingLength = 0;
|
|
113
145
|
// 循环处理,直到没有更多数据需要处理
|
|
114
146
|
while (this.receiveBuffer) {
|
|
115
147
|
// 检查并过滤心跳数据(处理粘包情况:心跳数据 + 协议数据)
|
|
116
|
-
if (this.
|
|
117
|
-
|
|
118
|
-
if (this.isHeartbeatData(first4Bytes)) {
|
|
119
|
-
// 移除前 4 字节的心跳数据,继续处理剩余数据
|
|
120
|
-
this.receiveBuffer =
|
|
121
|
-
this.receiveBuffer.length > HEARTBEAT_LENGTH ? this.receiveBuffer.slice(HEARTBEAT_LENGTH) : null;
|
|
122
|
-
continue; // 继续循环处理剩余数据
|
|
123
|
-
}
|
|
148
|
+
if (this.removeHeartbeatPrefix()) {
|
|
149
|
+
continue; // 继续循环处理剩余数据
|
|
124
150
|
}
|
|
125
151
|
// 提取所有完整协议包(自动校验)
|
|
126
152
|
const { packets, remaining, invalidPackets } = this.protocolHandler.extractAllCompletePackets(this.receiveBuffer);
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
this.emit('error', new Error(`Invalid packets detected: ${invalidPackets.length} packets dropped`));
|
|
130
|
-
}
|
|
131
|
-
// 处理每个有效完整包
|
|
132
|
-
for (const packet of packets) {
|
|
133
|
-
this.processPacket(packet);
|
|
134
|
-
}
|
|
153
|
+
// 处理提取出的协议包
|
|
154
|
+
this.processExtractedPackets(packets, invalidPackets);
|
|
135
155
|
// 更新接收缓冲区为剩余数据(半包数据保留)
|
|
136
156
|
this.receiveBuffer = remaining;
|
|
137
157
|
// 如果没有剩余数据,退出循环
|
|
138
158
|
if (!remaining || remaining.length === 0) {
|
|
139
159
|
break;
|
|
140
160
|
}
|
|
161
|
+
// 保护机制:检查 remaining 长度是否一直没变化
|
|
162
|
+
if (remaining.length > 0) {
|
|
163
|
+
if (remaining.length === lastRemainingLength) {
|
|
164
|
+
// 长度没变化,增加停滞计数
|
|
165
|
+
stagnantCount += 1;
|
|
166
|
+
if (stagnantCount > MAX_STAGNANT_LOOPS) {
|
|
167
|
+
this.receiveBuffer = null; // 丢弃剩余数据
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// 长度有变化,重置停滞计数
|
|
173
|
+
stagnantCount = 0;
|
|
174
|
+
lastRemainingLength = remaining.length;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
141
177
|
}
|
|
142
178
|
}
|
|
143
179
|
/**
|
|
@@ -213,6 +249,11 @@ export class SocketWrapper extends EventEmitter {
|
|
|
213
249
|
this.emit('error', new Error('Socket is not writable'));
|
|
214
250
|
return;
|
|
215
251
|
}
|
|
252
|
+
// 防止发送空数据:如果数据长度为 0,记录错误并返回
|
|
253
|
+
if (!bytes || bytes.length === 0) {
|
|
254
|
+
this.emit('error', new Error('尝试发送空数据包(size === 0),可能是协议构建失败'));
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
216
257
|
socket.write(bytes, (err) => {
|
|
217
258
|
if (err) {
|
|
218
259
|
this.emit('error', err);
|
|
@@ -242,7 +283,7 @@ export class SocketWrapper extends EventEmitter {
|
|
|
242
283
|
if (typeof buildOptions.seqId !== 'number') {
|
|
243
284
|
buildOptions.seqId = this.protocolHandler.generateSeqId();
|
|
244
285
|
}
|
|
245
|
-
const buffer = this.protocolHandler.build(
|
|
286
|
+
const buffer = this.protocolHandler.build(buildOptions);
|
|
246
287
|
const resolvedRequestId = buildOptions.seqId;
|
|
247
288
|
this.writeToSocket(buffer);
|
|
248
289
|
return resolvedRequestId;
|
|
@@ -278,4 +319,3 @@ export class SocketWrapper extends EventEmitter {
|
|
|
278
319
|
return this.tcpWrapper;
|
|
279
320
|
}
|
|
280
321
|
}
|
|
281
|
-
//# sourceMappingURL=SocketWrapper.js.map
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Uniform Protocol 协议处理模块
|
|
5
5
|
* 封装协议解析、校验和构建功能
|
|
6
6
|
*/
|
|
7
|
-
import { UniformProtocolParser } from '@unico/uniform-protocol-parser';
|
|
8
7
|
import { UniformProtocolBuilder } from '@unico/uniform-protocol-builder';
|
|
8
|
+
import { UniformProtocolParser } from '@unico/uniform-protocol-parser';
|
|
9
9
|
/**
|
|
10
10
|
* Uniform Protocol 协议处理器
|
|
11
11
|
*/
|
|
@@ -42,6 +42,7 @@ export class ProtocolHandler {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
catch (error) {
|
|
45
|
+
console.error('解析协议数据包时出错:', error);
|
|
45
46
|
throw error;
|
|
46
47
|
}
|
|
47
48
|
}
|
|
@@ -93,4 +94,3 @@ export class ProtocolHandler {
|
|
|
93
94
|
};
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
|
-
//# sourceMappingURL=protocolHandler.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as net from 'net';
|
|
2
|
-
import type {
|
|
2
|
+
import type { HeartbeatResponseChecker, SocketConnectOptions } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* tcp连接管理器
|
|
5
5
|
* 负责处理 TCP 连接、重连、超时检测等
|
|
@@ -33,7 +33,7 @@ export declare class TcpWrapper {
|
|
|
33
33
|
private pendingHeartbeat;
|
|
34
34
|
/** 连续心跳超时次数 */
|
|
35
35
|
private heartbeatTimeoutCount;
|
|
36
|
-
/**
|
|
36
|
+
/** 心跳响应识别函数(同时检查 ping 和 pong) */
|
|
37
37
|
private heartbeatResponseChecker;
|
|
38
38
|
/** 最大重连次数,0 表示无限制 */
|
|
39
39
|
private maxReconnectAttempts;
|
|
@@ -65,7 +65,6 @@ export declare class TcpWrapper {
|
|
|
65
65
|
private onError?;
|
|
66
66
|
/** 连接关闭回调(手动断开或重连失败时触发) */
|
|
67
67
|
private onClose?;
|
|
68
|
-
constructor();
|
|
69
68
|
/**
|
|
70
69
|
* 设置 Socket 事件监听
|
|
71
70
|
*/
|
|
@@ -110,6 +109,27 @@ export declare class TcpWrapper {
|
|
|
110
109
|
* 设置连接超时
|
|
111
110
|
*/
|
|
112
111
|
private setConnectTimeout;
|
|
112
|
+
/**
|
|
113
|
+
* 握手超时处理入口
|
|
114
|
+
* 提取为独立方法以降低 setConnectTimeout 的复杂度
|
|
115
|
+
*/
|
|
116
|
+
private handleHandshakeTimeout;
|
|
117
|
+
/**
|
|
118
|
+
* 是否还有剩余的握手重试次数
|
|
119
|
+
*/
|
|
120
|
+
private hasRemainingHandshakeAttempts;
|
|
121
|
+
/**
|
|
122
|
+
* 执行握手重试流程:销毁当前 socket、计算重试间隔并尝试重新连接
|
|
123
|
+
*/
|
|
124
|
+
private retryHandshakeConnection;
|
|
125
|
+
/**
|
|
126
|
+
* 安排一次新的握手重连尝试
|
|
127
|
+
*/
|
|
128
|
+
private scheduleHandshakeReconnect;
|
|
129
|
+
/**
|
|
130
|
+
* 握手多次超时后的失败处理
|
|
131
|
+
*/
|
|
132
|
+
private handleHandshakeFailure;
|
|
113
133
|
/**
|
|
114
134
|
* 清除连接超时定时器
|
|
115
135
|
*/
|
|
@@ -152,6 +172,10 @@ export declare class TcpWrapper {
|
|
|
152
172
|
* 设置心跳响应识别函数
|
|
153
173
|
*/
|
|
154
174
|
setHeartbeatResponseChecker(checker: HeartbeatResponseChecker): void;
|
|
175
|
+
/**
|
|
176
|
+
* 获取心跳响应识别函数
|
|
177
|
+
*/
|
|
178
|
+
getHeartbeatResponseChecker(): HeartbeatResponseChecker;
|
|
155
179
|
/**
|
|
156
180
|
* 获取当前连接地址
|
|
157
181
|
*/
|
|
@@ -165,4 +189,3 @@ export declare class TcpWrapper {
|
|
|
165
189
|
*/
|
|
166
190
|
getReconnectAttempts(): number;
|
|
167
191
|
}
|
|
168
|
-
//# sourceMappingURL=tcpWrapper.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ALL_CODE_LINE: 450
|
|
2
2
|
// AI_CODE_LINE: 40
|
|
3
3
|
import * as net from 'net';
|
|
4
|
-
import { DEFAULT_AUTO_RECONNECT,
|
|
4
|
+
import { DEFAULT_AUTO_RECONNECT, DEFAULT_ENABLE_EXPONENTIAL_BACKOFF, DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_HEARTBEAT_TIMEOUT, DEFAULT_MAX_RECONNECT_ATTEMPTS, DEFAULT_MAX_RECONNECT_INTERVAL, DEFAULT_RECONNECT_INTERVAL, } from './constants.js';
|
|
5
5
|
/**
|
|
6
6
|
* tcp连接管理器
|
|
7
7
|
* 负责处理 TCP 连接、重连、超时检测等
|
|
@@ -35,8 +35,13 @@ export class TcpWrapper {
|
|
|
35
35
|
pendingHeartbeat = false;
|
|
36
36
|
/** 连续心跳超时次数 */
|
|
37
37
|
heartbeatTimeoutCount = 0;
|
|
38
|
-
/**
|
|
38
|
+
/** 心跳响应识别函数(同时检查 ping 和 pong) */
|
|
39
39
|
heartbeatResponseChecker = (data) => {
|
|
40
|
+
// 检查 ping(发送的心跳)
|
|
41
|
+
if (data.length === 4 && data.toString('utf8') === 'ping') {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
// 检查 pong(心跳响应)
|
|
40
45
|
// data 是 Buffer 对象,需要转换为字符串比较
|
|
41
46
|
// 心跳响应可能是 "pong"(4字节)或 "pong\n"(5字节,带换行符)
|
|
42
47
|
if (data.length === 4) {
|
|
@@ -90,7 +95,6 @@ export class TcpWrapper {
|
|
|
90
95
|
onError;
|
|
91
96
|
/** 连接关闭回调(手动断开或重连失败时触发) */
|
|
92
97
|
onClose;
|
|
93
|
-
constructor() { }
|
|
94
98
|
/**
|
|
95
99
|
* 设置 Socket 事件监听
|
|
96
100
|
*/
|
|
@@ -103,6 +107,8 @@ export class TcpWrapper {
|
|
|
103
107
|
if (this.connectStartTime > 0) {
|
|
104
108
|
this.connectStartTime = 0; // 重置时间戳
|
|
105
109
|
}
|
|
110
|
+
// 禁用 Nagle 算法,确保小包立即发送(不延迟合并)
|
|
111
|
+
this.socket?.setNoDelay(true);
|
|
106
112
|
// 连接成功,重置所有计数器
|
|
107
113
|
this.reconnectAttempts = 0;
|
|
108
114
|
this.heartbeatTimeoutCount = 0;
|
|
@@ -121,7 +127,7 @@ export class TcpWrapper {
|
|
|
121
127
|
}
|
|
122
128
|
this.onData?.(data);
|
|
123
129
|
});
|
|
124
|
-
this.socket.on('close', (
|
|
130
|
+
this.socket.on('close', () => {
|
|
125
131
|
this.clearConnectTimeout();
|
|
126
132
|
this.stopHeartbeat();
|
|
127
133
|
this.cleanup();
|
|
@@ -206,7 +212,8 @@ export class TcpWrapper {
|
|
|
206
212
|
return;
|
|
207
213
|
}
|
|
208
214
|
// 增加重连计数
|
|
209
|
-
this.reconnectAttempts
|
|
215
|
+
this.reconnectAttempts += 1;
|
|
216
|
+
// 此时已经通过上面的检查,确保 address、port、connectOptions 都不为 null
|
|
210
217
|
try {
|
|
211
218
|
this.connect(this.address, this.port, this.connectOptions);
|
|
212
219
|
}
|
|
@@ -252,7 +259,7 @@ export class TcpWrapper {
|
|
|
252
259
|
this.clearHeartbeatTimeout();
|
|
253
260
|
this.heartbeatTimeout = setTimeout(() => {
|
|
254
261
|
if (this.pendingHeartbeat) {
|
|
255
|
-
this.heartbeatTimeoutCount
|
|
262
|
+
this.heartbeatTimeoutCount += 1;
|
|
256
263
|
// 只有连续2次心跳超时才清理socket
|
|
257
264
|
if (this.heartbeatTimeoutCount >= 2) {
|
|
258
265
|
this.connectionActive = false;
|
|
@@ -286,48 +293,88 @@ export class TcpWrapper {
|
|
|
286
293
|
setConnectTimeout() {
|
|
287
294
|
this.clearConnectTimeout();
|
|
288
295
|
this.connectTimeout = setTimeout(() => {
|
|
289
|
-
|
|
290
|
-
this.handshakeTimeoutAttempts++;
|
|
291
|
-
// 如果未达到最大尝试次数,重试连接
|
|
292
|
-
if (this.handshakeTimeoutAttempts < this.maxHandshakeTimeoutAttempts) {
|
|
293
|
-
// 清理当前socket
|
|
294
|
-
this.socket.destroy();
|
|
295
|
-
this.socket = null;
|
|
296
|
-
// 计算重试延迟(使用指数退避)
|
|
297
|
-
const retryDelay = this.calculateHandshakeRetryInterval();
|
|
298
|
-
// 重新尝试连接
|
|
299
|
-
if (this.address && this.port && this.connectOptions) {
|
|
300
|
-
setTimeout(() => {
|
|
301
|
-
if (this.connectionActive && !this.isConnected()) {
|
|
302
|
-
try {
|
|
303
|
-
// 重新创建连接
|
|
304
|
-
this.socket = net.createConnection({
|
|
305
|
-
host: this.address,
|
|
306
|
-
port: this.port,
|
|
307
|
-
keepAlive: true,
|
|
308
|
-
keepAliveInitialDelay: 1000,
|
|
309
|
-
});
|
|
310
|
-
// 重新设置超时和事件
|
|
311
|
-
this.setConnectTimeout();
|
|
312
|
-
this.setupSocketEvents();
|
|
313
|
-
}
|
|
314
|
-
catch (error) {
|
|
315
|
-
this.onError?.(error);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}, retryDelay);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
else {
|
|
322
|
-
// 超过最大尝试次数,断开连接
|
|
323
|
-
this.connectionActive = false;
|
|
324
|
-
this.socket.destroy();
|
|
325
|
-
this.onError?.(new Error(`Connection timeout after ${this.maxHandshakeTimeoutAttempts} attempts`));
|
|
326
|
-
this.onClose?.();
|
|
327
|
-
}
|
|
328
|
-
}
|
|
296
|
+
this.handleHandshakeTimeout();
|
|
329
297
|
}, 1000);
|
|
330
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* 握手超时处理入口
|
|
301
|
+
* 提取为独立方法以降低 setConnectTimeout 的复杂度
|
|
302
|
+
*/
|
|
303
|
+
handleHandshakeTimeout() {
|
|
304
|
+
// socket 已销毁或不存在时无需处理
|
|
305
|
+
if (!this.socket || this.socket.destroyed) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
this.handshakeTimeoutAttempts += 1;
|
|
309
|
+
if (this.hasRemainingHandshakeAttempts()) {
|
|
310
|
+
this.retryHandshakeConnection();
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
this.handleHandshakeFailure();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* 是否还有剩余的握手重试次数
|
|
318
|
+
*/
|
|
319
|
+
hasRemainingHandshakeAttempts() {
|
|
320
|
+
return this.handshakeTimeoutAttempts < this.maxHandshakeTimeoutAttempts;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* 执行握手重试流程:销毁当前 socket、计算重试间隔并尝试重新连接
|
|
324
|
+
*/
|
|
325
|
+
retryHandshakeConnection() {
|
|
326
|
+
// 清理当前 socket
|
|
327
|
+
this.socket?.destroy();
|
|
328
|
+
this.socket = null;
|
|
329
|
+
// 计算重试延迟(使用指数退避)
|
|
330
|
+
const retryDelay = this.calculateHandshakeRetryInterval();
|
|
331
|
+
// 保存连接信息到局部变量,避免在异步回调中使用可变的实例属性
|
|
332
|
+
const address = this.address;
|
|
333
|
+
const port = this.port;
|
|
334
|
+
const connectOptions = this.connectOptions;
|
|
335
|
+
if (!address || !port || !connectOptions) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
this.scheduleHandshakeReconnect(address, port, retryDelay);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* 安排一次新的握手重连尝试
|
|
342
|
+
*/
|
|
343
|
+
scheduleHandshakeReconnect(address, port, retryDelay) {
|
|
344
|
+
setTimeout(() => {
|
|
345
|
+
if (!this.connectionActive || this.isConnected()) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
this.socket = new net.Socket({
|
|
350
|
+
readableHighWaterMark: 1024 * 1024, // 1024KB 读取缓冲区
|
|
351
|
+
writableHighWaterMark: 1024 * 1024, // 1024KB 写入缓冲区
|
|
352
|
+
});
|
|
353
|
+
// 连接到服务器
|
|
354
|
+
this.socket.connect({
|
|
355
|
+
host: address,
|
|
356
|
+
port,
|
|
357
|
+
keepAlive: true,
|
|
358
|
+
keepAliveInitialDelay: 1000,
|
|
359
|
+
});
|
|
360
|
+
// 重新设置超时和事件
|
|
361
|
+
this.setConnectTimeout();
|
|
362
|
+
this.setupSocketEvents();
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
this.onError?.(error);
|
|
366
|
+
}
|
|
367
|
+
}, retryDelay);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* 握手多次超时后的失败处理
|
|
371
|
+
*/
|
|
372
|
+
handleHandshakeFailure() {
|
|
373
|
+
this.connectionActive = false;
|
|
374
|
+
this.socket?.destroy();
|
|
375
|
+
this.onError?.(new Error(`Connection timeout after ${this.maxHandshakeTimeoutAttempts} attempts`));
|
|
376
|
+
this.onClose?.();
|
|
377
|
+
}
|
|
331
378
|
/**
|
|
332
379
|
* 清除连接超时定时器
|
|
333
380
|
*/
|
|
@@ -396,12 +443,16 @@ export class TcpWrapper {
|
|
|
396
443
|
}
|
|
397
444
|
// 记录连接开始时间(用于计算握手时间)
|
|
398
445
|
this.connectStartTime = Date.now();
|
|
399
|
-
|
|
400
|
-
|
|
446
|
+
this.socket = new net.Socket({
|
|
447
|
+
readableHighWaterMark: 1024 * 1024, // 1024KB 读取缓冲区
|
|
448
|
+
writableHighWaterMark: 1024 * 1024, // 1024KB 写入缓冲区
|
|
449
|
+
});
|
|
450
|
+
// 连接到服务器
|
|
451
|
+
this.socket.connect({
|
|
401
452
|
host,
|
|
402
453
|
port,
|
|
403
454
|
keepAlive: true,
|
|
404
|
-
keepAliveInitialDelay: 1000,
|
|
455
|
+
keepAliveInitialDelay: 1000,
|
|
405
456
|
});
|
|
406
457
|
// 设置连接超时( 1000ms)
|
|
407
458
|
this.setConnectTimeout();
|
|
@@ -450,6 +501,12 @@ export class TcpWrapper {
|
|
|
450
501
|
setHeartbeatResponseChecker(checker) {
|
|
451
502
|
this.heartbeatResponseChecker = checker || ((data) => data.toString() === 'pong');
|
|
452
503
|
}
|
|
504
|
+
/**
|
|
505
|
+
* 获取心跳响应识别函数
|
|
506
|
+
*/
|
|
507
|
+
getHeartbeatResponseChecker() {
|
|
508
|
+
return this.heartbeatResponseChecker;
|
|
509
|
+
}
|
|
453
510
|
/**
|
|
454
511
|
* 获取当前连接地址
|
|
455
512
|
*/
|
|
@@ -469,4 +526,3 @@ export class TcpWrapper {
|
|
|
469
526
|
return this.reconnectAttempts;
|
|
470
527
|
}
|
|
471
528
|
}
|
|
472
|
-
//# sourceMappingURL=tcpWrapper.js.map
|