初始化项目
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 协议帧编解码基座(1.4.11)。
|
||||
*
|
||||
* 帧格式(小端):
|
||||
* [0..3] uint32 整帧长度(含头,6 + payload)
|
||||
* [4..5] uint16 序号
|
||||
* [6..] bytes 载荷
|
||||
*
|
||||
* 该格式与 MySQL / MariaDB 的 3 字节长度头不同,是引擎无关的内部封装层:
|
||||
* 各引擎协议实现(1.4.3–1.4.10)在载荷里携带自己的报文,本层只负责
|
||||
* 传输分帧、粘包拆分与错误长度防御。
|
||||
*/
|
||||
|
||||
import { ConnectionError } from '../connection/types';
|
||||
|
||||
/** 一帧数据。 */
|
||||
export class Frame {
|
||||
readonly seq: number;
|
||||
readonly payload: Uint8Array;
|
||||
|
||||
constructor(seq: number, payload: Uint8Array) {
|
||||
this.seq = seq;
|
||||
this.payload = payload;
|
||||
}
|
||||
}
|
||||
|
||||
export class FrameCodec {
|
||||
/** 头长度:4 字节长度 + 2 字节序号。 */
|
||||
static readonly HEADER_BYTES: number = 6;
|
||||
/** 单帧载荷上限:16 MB。 */
|
||||
static readonly MAX_PAYLOAD_BYTES: number = 16 * 1024 * 1024;
|
||||
|
||||
/** 组帧。载荷超限时抛 ConnectionError。 */
|
||||
static encode(seq: number, payload: Uint8Array): Uint8Array {
|
||||
if (payload.length > FrameCodec.MAX_PAYLOAD_BYTES) {
|
||||
throw new ConnectionError('payload too large: ' + payload.length);
|
||||
}
|
||||
const total: number = FrameCodec.HEADER_BYTES + payload.length;
|
||||
const out: Uint8Array = new Uint8Array(total);
|
||||
out[0] = total & 0xFF;
|
||||
out[1] = (total >>> 8) & 0xFF;
|
||||
out[2] = (total >>> 16) & 0xFF;
|
||||
out[3] = (total >>> 24) & 0xFF;
|
||||
out[4] = seq & 0xFF;
|
||||
out[5] = (seq >>> 8) & 0xFF;
|
||||
out.set(payload, FrameCodec.HEADER_BYTES);
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 读取头部声明的整帧长度;不足 4 字节返回 -1。 */
|
||||
static frameLength(header: Uint8Array): number {
|
||||
if (header.length < 4) {
|
||||
return -1;
|
||||
}
|
||||
return header[0] | (header[1] << 8) | (header[2] << 16) | (header[3] << 24);
|
||||
}
|
||||
|
||||
/** 解出第一帧;数据不足返回 undefined(由调用方继续收)。 */
|
||||
static decode(buffer: Uint8Array): Frame | undefined {
|
||||
const total: number = FrameCodec.frameLength(buffer);
|
||||
if (total < 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (total < FrameCodec.HEADER_BYTES || total > FrameCodec.HEADER_BYTES + FrameCodec.MAX_PAYLOAD_BYTES) {
|
||||
throw new ConnectionError('invalid frame length: ' + total);
|
||||
}
|
||||
if (buffer.length < total) {
|
||||
return undefined;
|
||||
}
|
||||
const seq: number = buffer[4] | (buffer[5] << 8);
|
||||
return new Frame(seq, buffer.slice(FrameCodec.HEADER_BYTES, total));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user