feat(frame): go-hua/common 帧协议独立成包并修复两处缺陷

- SetData 超长时 fmt.Errorf 结果未 return,超 65535 字节静默放行 → 改为返回错误
- ReadFrame 原实现 conn.Read 直读帧头(短读错位)且一次性 bufio.Reader 会丢弃
  缓冲中的后续帧字节 → 统一改 io.ReadFull 精确读取,新增连续两帧回归测试
- 补齐单元测试(往返/流水线/超长/Marshal/RawHeader)、examples/frame 例程与 README
This commit is contained in:
2026-09-20 12:20:51 +08:00
parent 1c9c717579
commit 9d7a512e18
4 changed files with 374 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
# frame
应用层帧协议:头 8 字节(版本 1 + 命令 1 + 长度 2 + 流 ID 4,小端序)+ 数据,
提供帧与 `net.Conn` 间的读写封装,配合 JSON 编解码使用。
> 迁移自 go-hua/common/frame.go,独立成包。
## 帧格式
```
+---------+---------+-----------+-----------+----------------+
| ver (1) | cmd (1) | length(2) | sid (4) | data (length) |
+---------+---------+-----------+-----------+----------------+
小端序,length 最大 65535
```
## 用法
```go
import (
"net"
"git.zeroonesoft.cn/golib/zogo/frame"
)
// 组帧:版本 + 命令字 + 流 ID,数据可 Marshal 任意 JSON 值
f := frame.NewFrame(frame.FrameVersion, 0x10, 42)
f.Marshal(map[string]any{"msg": "hello"})
// 连接上写帧 / 读帧(阻塞直到完整帧)
frame.WriteFrame(conn, f)
got, err := frame.ReadFrame(conn)
var data map[string]any
got.Unmarshal(&data)
```
完整可运行例程:[examples/frame/main.go](../examples/frame/main.go)
## 迁移修复(相对 go-hua/common 原实现)
1. **SetData 超长未报错**:原实现 `fmt.Errorf` 结果未 return,超长数据静默截断写入;
现超 65535 字节正确返回错误(有回归测试)。
2. **ReadFrame 短读/吞字节**:原实现直接 `conn.Read` 读头(TCP 分段会错位),且用
一次性 `bufio.Reader` 读体(缓冲里多读到的后续帧字节随 reader 丢弃)。现统一改用
`io.ReadFull` 精确读取,连续多帧不丢数据(有回归测试)。
## 注意
- 单帧数据上限 65535 字节(length 字段 2 字节),更大 payload 请业务侧分片。
- `ReadFrame`/`WriteFrame` 非并发安全,同一连接的读与写可各由一个 goroutine 持有。