Laya.Byte 二进制数据处理
简介
Laya.Byte 类提供了用于二进制数据的读取、写入和处理的方法。该类适合需要在字节级别访问数据的场景,如网络通信、文件解析、协议实现等。
适用场景:
- WebSocket/Socket 网络通信的数据封包和解包
- 二进制文件格式解析(如图片、音频、自定义格式)
- 游戏协议的序列化与反序列化
- 与后端二进制接口的数据交互
工作原理:
写入数据 → 移动 pos 指针 → 重置 pos → 读取数据 → 循环
核心优势:
| 优势 |
说明 |
| 高效的二进制操作 |
直接操作字节,性能优异 |
| 支持多种数据类型 |
整数、浮点数、字符串、数组 |
| 灵活的字节序 |
支持大端序和小端序转换 |
| 便于网络通信 |
与 Socket 无缝配合 |
目录
API 参考
静态属性
| 属性 |
类型 |
值 |
描述 |
BIG_ENDIAN |
string |
"bigEndian" |
大端字节序(网络字节序) |
LITTLE_ENDIAN |
string |
"littleEndian" |
小端字节序 |
静态方法
| 方法 |
返回值 |
描述 |
getSystemEndian() |
string |
获取当前系统的字节序 |
实例属性
| 属性 |
类型 |
描述 |
buffer |
ArrayBuffer |
获取有效数据的 ArrayBuffer |
rawBuffer |
ArrayBuffer |
获取 ArrayBuffer 引用 |
endian |
string |
实例的字节序,可读写 |
length |
number |
Byte 对象的长度(字节) |
pos |
number |
读写指针的当前位置 |
bytesAvailable |
number |
可读取的剩余字节数 |
整数读写
| 方法 |
参数 |
返回值 |
描述 |
writeByte(value) |
number |
void |
写入带符号字节(-128~127) |
readByte() |
- |
number |
读取带符号字节 |
writeUint8(value) |
number |
void |
写入无符号 8 位整数 |
readUint8() |
- |
number |
读取无符号 8 位整数 |
writeInt16(value) |
number |
void |
写入带符号 16 位整数 |
readInt16() |
- |
number |
读取带符号 16 位整数 |
writeUint16(value) |
number |
void |
写入无符号 16 位整数 |
readUint16() |
- |
number |
读取无符号 16 位整数 |
writeInt32(value) |
number |
void |
写入带符号 32 位整数 |
readInt32() |
- |
number |
读取带符号 32 位整数 |
writeUint32(value) |
number |
void |
写入无符号 32 位整数 |
readUint32() |
- |
number |
读取无符号 32 位整数 |
浮点数读写
| 方法 |
参数 |
返回值 |
描述 |
writeFloat32(value) |
number |
void |
写入 IEEE 754 单精度浮点数 |
readFloat32() |
- |
number |
读取 IEEE 754 单精度浮点数 |
writeFloat64(value) |
number |
void |
写入 IEEE 754 双精度浮点数 |
readFloat64() |
- |
number |
读取 IEEE 754 双精度浮点数 |
字符串读写
| 方法 |
参数 |
返回值 |
描述 |
writeUTFBytes(value) |
string |
void |
写入 UTF-8 字符串,无长度前缀 |
readUTFBytes(len?) |
number |
string |
读取 UTF-8 字节序列 |
writeUTFString(value) |
string |
void |
先写入 16 位长度,再写入字符串 |
readUTFString() |
- |
string |
读取 UTF-8 字符串 |
writeUTFString32(value) |
string |
void |
先写入 32 位长度,再写入字符串 |
readUTFString32() |
- |
string |
读取 UTF-8 字符串(32位长度) |
readString() |
- |
string |
先读 Uint16 长度,再读字符串 |
数组读写
| 方法 |
参数 |
返回值 |
描述 |
readUint8Array(start, len) |
number, number |
Uint8Array |
读取 Uint8Array |
readInt8Array(start, len) |
number, number |
Int8Array |
读取 Int8Array |
readInt16Array(start, len) |
number, number |
Int16Array |
读取 Int16Array |
readFloat32Array(start, len) |
number, number |
Float32Array |
读取 Float32Array |
writeArrayBuffer(buf, off, len) |
ArrayBuffer, ... |
void |
写入 ArrayBuffer 数据 |
readArrayBuffer(length) |
number |
ArrayBuffer |
读取 ArrayBuffer |
工具方法
| 方法 |
描述 |
clear() |
清除内容,重置 length 和 pos 为 0 |
基础用法
1. 基本读写
最简单的数据写入和读取:
// 创建 Byte 对象
let byte = new Laya.Byte();
// 写入数据
byte.writeInt32(123456);
byte.writeFloat32(3.14);
byte.writeUTFString("Hello LayaAir");
// 重置位置以便读取
byte.pos = 0;
// 读取数据
let intVal = byte.readInt32(); // 123456
let floatVal = byte.readFloat32(); // 3.14
let strVal = byte.readUTFString(); // "Hello LayaAir"
console.log(intVal, floatVal, strVal);
2. 字节序处理
跨平台通信时需要注意字节序:
let byte = new Laya.Byte();
// 获取系统字节序
let sysEndian = Laya.Byte.getSystemEndian();
console.log("系统字节序:", sysEndian); // "littleEndian" 或 "bigEndian"
// 设置字节序
byte.endian = Laya.Byte.BIG_ENDIAN;
byte.writeUint16(0x1234);
byte.pos = 0;
let value = byte.readUint16();
console.log("读取值:", value.toString(16)); // "1234"
3. 从 ArrayBuffer 创建
// 从现有 ArrayBuffer 创建
let arrayBuffer = new ArrayBuffer(100);
let byte = new Laya.Byte(arrayBuffer);
// 或指定初始大小
let byte2 = new Laya.Byte(1024);
实用示例
示例1: 自定义协议封包
export class PacketCodec {
// 协议头定义
private static readonly HEADER = "PB";
private static readonly VERSION = 1;
// 封包:消息类型 + 数据
public static encode(msgType: number, data: string): Laya.Byte {
let byte = new Laya.Byte();
// 写入协议头
byte.writeUTFBytes(this.HEADER);
byte.writeUint8(this.VERSION);
// 写入消息类型
byte.writeUint16(msgType);
// 写入数据内容
byte.writeUTFString(data);
return byte;
}
// 解包:解析消息类型和数据
public static decode(byte: Laya.Byte): { msgType: number, data: string } | null {
byte.pos = 0;
// 验证协议头
let header = byte.readUTFBytes(2);
if (header !== this.HEADER) {
console.error("无效的协议头");
return null;
}
// 读取版本
let version = byte.readUint8();
if (version !== this.VERSION) {
console.error("不支持的协议版本:", version);
return null;
}
// 读取消息类型和数据
let msgType = byte.readUint16();
let data = byte.readUTFString();
return { msgType, data };
}
}
// 使用示例
let packet = PacketCodec.encode(1001, "Hello Server");
let decoded = PacketCodec.decode(packet);
console.log(decoded); // { msgType: 1001, data: "Hello Server" }
示例2: WebSocket 通信
@regClass()
export class NetworkManager extends Laya.Script {
private socket: Laya.Socket;
private output: Laya.Byte;
onStart(): void {
this.socket = new Laya.Socket();
this.socket.connectByUrl("wss://game-server.com:443");
this.output = this.socket.output;
// 监听事件
this.socket.on(Laya.Event.OPEN, this, this.onOpen);
this.socket.on(Laya.Event.MESSAGE, this, this.onMessage);
this.socket.on(Laya.Event.CLOSE, this, this.onClose);
this.socket.on(Laya.Event.ERROR, this, this.onError);
}
private onOpen(): void {
console.log("连接成功");
// 发送登录请求
this.sendLogin("player001", "token123");
}
// 发送登录请求
public sendLogin(username: string, token: string): void {
this.output.writeUint16(1001); // 消息类型:登录
this.output.writeUTFString(username);
this.output.writeUTFString(token);
this.socket.flush();
}
// 发送位置同步
public sendPosition(x: number, y: number): void {
this.output.writeUint16(2001); // 消息类型:位置同步
this.output.writeFloat32(x);
this.output.writeFloat32(y);
this.socket.flush();
}
private onMessage(msg: any): void {
if (typeof msg === "string") {
console.log("收到文本消息:", msg);
} else if (msg instanceof ArrayBuffer) {
// 解析二进制消息
let byte = new Laya.Byte(msg);
this.handleBinaryMessage(byte);
}
this.socket.input.clear();
}
private handleBinaryMessage(byte: Laya.Byte): void {
let msgType = byte.readUint16();
switch (msgType) {
case 1001:
this.handleLoginResponse(byte);
break;
case 3001:
this.handlePlayerState(byte);
break;
default:
console.warn("未知消息类型:", msgType);
}
}
private handleLoginResponse(byte: Laya.Byte): void {
let success = byte.readUint8() === 1;
let playerId = byte.readUTFString();
console.log("登录响应:", success, playerId);
}
private handlePlayerState(byte: Laya.Byte): void {
let playerId = byte.readUTFString();
let x = byte.readFloat32();
let y = byte.readFloat32();
console.log("玩家状态:", playerId, x, y);
}
private onClose(): void {
console.log("连接关闭");
}
private onError(): void {
console.log("连接错误");
}
onDestroy(): void {
this.socket.close();
this.socket.offAll();
}
}
示例3: 二进制文件解析
export class BinaryFileParser {
// 解析自定义二进制文件
public static parse(arrayBuffer: ArrayBuffer): any {
let byte = new Laya.Byte(arrayBuffer);
byte.endian = Laya.Byte.LITTLE_ENDIAN;
// 读取文件头
let magic = byte.readUTFBytes(4);
if (magic !== "DATA") {
throw new Error("无效的文件格式");
}
let version = byte.readUint16();
let recordCount = byte.readUint32();
let records = [];
// 读取记录
for (let i = 0; i < recordCount; i++) {
let record = this.readRecord(byte);
records.push(record);
}
return { version, records };
}
private static readRecord(byte: Laya.Byte): any {
let id = byte.readInt32();
let type = byte.readUint8();
let x = byte.readFloat32();
let y = byte.readFloat32();
let name = byte.readUTFString();
return { id, type, x, y, name };
}
// 写入二进制文件
public static write(records: any[]): Laya.Byte {
let byte = new Laya.Byte();
byte.endian = Laya.Byte.LITTLE_ENDIAN;
// 写入文件头
byte.writeUTFBytes("DATA");
byte.writeUint16(1); // 版本
byte.writeUint32(records.length);
// 写入记录
for (let record of records) {
byte.writeInt32(record.id);
byte.writeUint8(record.type);
byte.writeFloat32(record.x);
byte.writeFloat32(record.y);
byte.writeUTFString(record.name);
}
return byte;
}
}
// 使用示例
let data = [
{ id: 1, type: 0, x: 100, y: 200, name: "对象1" },
{ id: 2, type: 1, x: 300, y: 400, name: "对象2" }
];
let fileData = BinaryFileParser.write(data);
let parsed = BinaryFileParser.parse(fileData.buffer);
console.log(parsed);
示例4: 位置数据回写
// 先写入数据,再回写长度
let byte = new Laya.Byte();
// 写入标识
byte.writeUTFBytes("HEAD");
// 预留长度位置(4字节)
byte.writeInt32(0);
let lengthPos = byte.pos - 4;
// 写入数据内容
byte.writeInt32(100);
byte.writeFloat32(3.14);
byte.writeUTFString("Hello");
// 回写数据长度
let currentPos = byte.pos;
let dataLength = currentPos - lengthPos - 4;
byte.pos = lengthPos;
byte.writeInt32(dataLength);
byte.pos = currentPos;
// 读取验证
byte.pos = 0;
console.log(byte.readUTFBytes(4)); // "HEAD"
console.log(byte.readInt32()); // 数据长度
示例5: 批量数据处理
// 批量写入顶点数据
let byte = new Laya.Byte();
let vertices = [
{ x: 0, y: 0, z: 0 },
{ x: 100, y: 0, z: 0 },
{ x: 0, y: 100, z: 0 },
{ x: 100, y: 100, z: 0 }
];
// 写入顶点数量
byte.writeUint32(vertices.length);
// 写入顶点数据
for (let v of vertices) {
byte.writeFloat32(v.x);
byte.writeFloat32(v.y);
byte.writeFloat32(v.z);
}
// 批量读取
byte.pos = 0;
let count = byte.readUint32();
let loadedVertices = [];
for (let i = 0; i < count; i++) {
loadedVertices.push({
x: byte.readFloat32(),
y: byte.readFloat32(),
z: byte.readFloat32()
});
}
console.log(loadedVertices);
高级技巧
1. 使用常量定义协议字段
export class Protocol {
// 消息类型
public static readonly MSG_LOGIN = 1001;
public static readonly MSG_MOVE = 1002;
public static readonly MSG_ATTACK = 1003;
// 数据偏移量
public static readonly OFFSET_MSG_TYPE = 0;
public static readonly OFFSET_MSG_LEN = 2;
public static readonly OFFSET_MSG_DATA = 4;
}
2. 创建 Byte 复用池
export class BytePool {
private static pool: Laya.Byte[] = [];
public static get(): Laya.Byte {
if (this.pool.length > 0) {
let byte = this.pool.pop();
byte.clear();
return byte;
}
return new Laya.Byte();
}
public static recover(byte: Laya.Byte): void {
if (this.pool.length < 50) {
this.pool.push(byte);
}
}
}
3. 检查可读数据
function safeRead(byte: Laya.Byte): void {
// 检查是否有足够数据可读
if (byte.bytesAvailable < 12) {
console.warn("数据不足");
return;
}
// 安全读取
let a = byte.readInt32();
let b = byte.readFloat32();
let c = byte.readUint32();
}
4. 保存和恢复读取位置
function peekData(byte: Laya.Byte): number {
// 保存当前位置
let savedPos = byte.pos;
// 读取数据
let value = byte.readInt32();
// 恢复位置
byte.pos = savedPos;
return value;
}
最佳实践
1. 读写配对原则
// ✅ 正确:读写顺序一致
byte.writeInt32(100);
byte.writeFloat32(3.14);
byte.writeUTFString("Hello");
byte.pos = 0;
let a = byte.readInt32();
let b = byte.readFloat32();
let c = byte.readUTFString();
// ❌ 错误:读写顺序不一致会导致数据错乱
2. 网络通信统一字节序
// 网络通信建议统一使用大端序
let byte = new Laya.Byte();
byte.endian = Laya.Byte.BIG_ENDIAN; // 网络字节序
3. 使用完毕后清理
// 网络消息处理完成后清理
let byte = new Laya.Byte(msg);
// ... 处理数据 ...
byte.clear(); // 释放内存
4. 字符串方法选择
| 方法 |
适用场景 |
特点 |
writeUTFString / readUTFString |
网络协议 |
带 16 位长度前缀 |
writeUTFString32 / readUTFString32 |
大数据传输 |
带 32 位长度前缀 |
writeUTFBytes / readUTFBytes |
固定长度或已知长度 |
无长度前缀,需指定长度 |
5. 错误处理
export class SafeByteReader {
public static readInt32Safe(byte: Laya.Byte): number | null {
if (byte.bytesAvailable < 4) {
console.error("数据不足,无法读取 Int32");
return null;
}
return byte.readInt32();
}
public static readStringSafe(byte: Laya.Byte): string | null {
try {
return byte.readUTFString();
} catch (e) {
console.error("读取字符串失败:", e);
return null;
}
}
}
注意事项
- 必须使用
Laya.Byte 前缀 :访问该类时需加上 Laya. 命名空间
- 读写顺序一致:写入和读取的顺序必须对应
- 注意 pos 指针:读写操作会自动移动 pos,读取前记得重置
- 字节序问题:跨平台通信需确保字节序一致
- 内存管理 :处理完成后调用
clear() 释放内存
- 避免废弃方法 :使用
read* 方法替代 get* 方法
相关文档