feat(framework): net NetClient 可选 debug 收发包日志
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,8 @@ export interface NetClientOptions {
|
||||
transportFactory: () => Transport;
|
||||
clock?: Clock;
|
||||
bus?: EventBus<NetClientEvents>;
|
||||
debug?: boolean; // 为真时打印每帧 send/recv
|
||||
logger?: (...args: unknown[]) => void; // 日志函数,默认 console.log(便于测试注入)
|
||||
}
|
||||
|
||||
/** 网络客户端:编排 transport/codec/heartbeat/reconnect,事件驱动,忠实 docs/protocol/01。 */
|
||||
@@ -41,6 +43,8 @@ export class NetClient {
|
||||
private loginGuard: unknown = null;
|
||||
private reconnectTimer: unknown = null;
|
||||
private stopped = false;
|
||||
private debug: boolean;
|
||||
private logger: (...args: unknown[]) => void;
|
||||
|
||||
constructor(opts: NetClientOptions) {
|
||||
this.bus = opts.bus ?? new EventBus<NetClientEvents>();
|
||||
@@ -48,6 +52,8 @@ export class NetClient {
|
||||
this.clock = opts.clock ?? realClock;
|
||||
this.policy = new ReconnectPolicy(opts.servers, SERVER_ROTATE_EVERY);
|
||||
this.watchdog = new HeartbeatWatchdog(RECV_TIMEOUT_MS, () => this.onRecvTimeout(), this.clock);
|
||||
this.debug = opts.debug ?? false;
|
||||
this.logger = opts.logger ?? console.log;
|
||||
}
|
||||
|
||||
setIdentity(identity: LoginRequestData): void { this.identity = identity; }
|
||||
@@ -66,7 +72,13 @@ export class NetClient {
|
||||
|
||||
/** 发业务包(单层信封)。 */
|
||||
send(route: string, rpc: string, data: unknown): void {
|
||||
this.transport?.send(encodeOutbound(route, rpc, data));
|
||||
this.sendFrame(encodeOutbound(route, rpc, data));
|
||||
}
|
||||
|
||||
/** 统一发送切面:debug 时打印出站帧。 */
|
||||
private sendFrame(frame: string): void {
|
||||
if (this.debug) this.logger('[net] →', frame);
|
||||
this.transport?.send(frame);
|
||||
}
|
||||
|
||||
private connect(url: string): void {
|
||||
@@ -87,12 +99,13 @@ export class NetClient {
|
||||
private sendLogin(): void {
|
||||
if (!this.identity) return; // 无身份不发(docs §6.1:首登需身份)
|
||||
this.isSendLoginState = true;
|
||||
this.transport?.send(encodeOutbound(Route.agent, 'player_login', this.identity));
|
||||
this.sendFrame(encodeOutbound(Route.agent, 'player_login', this.identity));
|
||||
if (this.loginGuard != null) this.clock.clearTimeout(this.loginGuard);
|
||||
this.loginGuard = this.clock.setTimeout(() => { this.loginGuard = null; this.onLoginGuardTimeout(); }, LOGIN_GUARD_MS); // docs §6.2
|
||||
}
|
||||
|
||||
private onMessage(frame: string): void {
|
||||
if (this.debug) this.logger('[net] ←', frame);
|
||||
const r = decodeFrame(frame);
|
||||
if (r.kind === 'ignore') return;
|
||||
this.watchdog.feed(); // 收任意有效帧重置看门狗 docs §4.1
|
||||
|
||||
@@ -123,3 +123,41 @@ test('TcpID 去重:旧连接的残留包被丢弃', async () => {
|
||||
await t1.flush();
|
||||
assert.equal(messages.length, 0);
|
||||
});
|
||||
|
||||
test('debug=true 记录 send 与 recv(注入 logger)', async () => {
|
||||
const t = new FakeTransport();
|
||||
const { clock } = fakeClock();
|
||||
const logs: unknown[][] = [];
|
||||
const client = new NetClient({
|
||||
servers: 'ws://a',
|
||||
transportFactory: () => t,
|
||||
clock,
|
||||
debug: true,
|
||||
logger: (...a: unknown[]) => logs.push(a),
|
||||
});
|
||||
client.setIdentity(IDENTITY);
|
||||
client.start();
|
||||
await t.flush();
|
||||
assert.ok(logs.some((l) => l[0] === '[net] →')); // 发出 player_login 被记录
|
||||
t.serverPush(JSON.stringify({ data: JSON.stringify({ route: 'agent', rpc: 'player_login', data: { state: 0, playerid: 9 } }) }));
|
||||
await t.flush();
|
||||
assert.ok(logs.some((l) => l[0] === '[net] ←')); // 收包被记录
|
||||
});
|
||||
|
||||
test('debug 默认关闭:logger 不被调用', async () => {
|
||||
const t = new FakeTransport();
|
||||
const { clock } = fakeClock();
|
||||
const logs: unknown[][] = [];
|
||||
const client = new NetClient({
|
||||
servers: 'ws://a',
|
||||
transportFactory: () => t,
|
||||
clock,
|
||||
logger: (...a: unknown[]) => logs.push(a),
|
||||
});
|
||||
client.setIdentity(IDENTITY);
|
||||
client.start();
|
||||
await t.flush();
|
||||
t.serverPush(JSON.stringify({ data: JSON.stringify({ route: 'agent', rpc: 'player_login', data: { state: 0, playerid: 9 } }) }));
|
||||
await t.flush();
|
||||
assert.equal(logs.length, 0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user