feat(proto): 自 go-hua 迁入长度前缀封包协议编解码
- 8 字节头(4 字节 ID/校验 + 4 字节长度, 小端序) + CRC32 校验版本 - 附测试、examples/proto 例程与包 README(含 Decode reader 复用告诫)
This commit is contained in:
+213
@@ -0,0 +1,213 @@
|
||||
// Package proto 提供长度前缀封包协议的编解码(8 字节头:4 字节 ID/校验 + 4 字节长度,小端序),支持 CRC32 校验版本。
|
||||
package proto
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
// PakHeadLen 消息头长度:普通版为 ID 4 字节 + 长度 4 字节;CRC32 版为校验值 4 字节 + 长度 4 字节
|
||||
const (
|
||||
PakHeadLen int32 = 8
|
||||
)
|
||||
|
||||
// Encode 编码
|
||||
func Encode(buf []byte) ([]byte, error) {
|
||||
// 读取消息的长度,转换成int32类型(占4个字节)
|
||||
var id int32 = 5
|
||||
var length = int32(len(buf))
|
||||
var pkg = new(bytes.Buffer)
|
||||
// 写入消息头
|
||||
err := binary.Write(pkg, binary.LittleEndian, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(pkg, binary.LittleEndian, length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 写入消息实体
|
||||
err = binary.Write(pkg, binary.LittleEndian, []byte(buf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pkg.Bytes(), nil
|
||||
}
|
||||
|
||||
// Decode 解码
|
||||
func Decode(reader *bufio.Reader) ([]byte, error) {
|
||||
// 读取头部
|
||||
lengthByte, err := reader.Peek(int(PakHeadLen)) // 读取前4个字节的数据
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lengthBuff := bytes.NewBuffer(lengthByte)
|
||||
var id int32 = 0
|
||||
var length int32 = 0
|
||||
|
||||
err = binary.Read(lengthBuff, binary.LittleEndian, &id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(lengthBuff, binary.LittleEndian, &length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
return nil, errors.New("decode length cannot be less than 0")
|
||||
}
|
||||
|
||||
if length > 0x1000000 {
|
||||
return nil, errors.New("decode length cannot be greater than 0x1000000")
|
||||
}
|
||||
|
||||
var data []byte
|
||||
|
||||
// 剩余读长长度
|
||||
unread := int(PakHeadLen + length)
|
||||
readLen := 0
|
||||
for {
|
||||
if unread > 0x10000 {
|
||||
readLen = 0x10000
|
||||
} else {
|
||||
readLen = unread
|
||||
}
|
||||
|
||||
_, err = reader.Peek(readLen) // 先窥探数据
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Buffered返回缓冲中现有的可读取的字节数。
|
||||
if reader.Buffered() < readLen {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 读取
|
||||
buf := make([]byte, readLen)
|
||||
_, err = reader.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = append(data, buf...)
|
||||
|
||||
unread -= readLen
|
||||
//fmt.Println("readLen", readLen)
|
||||
if unread <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(data) < int(PakHeadLen) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return data[PakHeadLen:], nil
|
||||
}
|
||||
|
||||
// EncodeCrc32 编码 Crc32校验
|
||||
func EncodeCrc32(buf []byte) ([]byte, error) {
|
||||
// 读取消息的长度,转换成int32类型(占4个字节)
|
||||
ieee := crc32.NewIEEE()
|
||||
ieee.Write(buf)
|
||||
var crc = ieee.Sum32()
|
||||
var length = int32(len(buf))
|
||||
var pkg = new(bytes.Buffer)
|
||||
// 写入消息头
|
||||
err := binary.Write(pkg, binary.LittleEndian, crc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(pkg, binary.LittleEndian, length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 写入消息实体
|
||||
err = binary.Write(pkg, binary.LittleEndian, []byte(buf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pkg.Bytes(), nil
|
||||
}
|
||||
|
||||
// DecodeCrc32 解码 Crc32校验
|
||||
func DecodeCrc32(reader *bufio.Reader) ([]byte, error) {
|
||||
// 读取头部
|
||||
lengthByte, err := reader.Peek(int(PakHeadLen)) // 读取前4个字节的数据
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lengthBuff := bytes.NewBuffer(lengthByte)
|
||||
var crc uint32 = 0
|
||||
var length int32 = 0
|
||||
|
||||
err = binary.Read(lengthBuff, binary.LittleEndian, &crc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(lengthBuff, binary.LittleEndian, &length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
return nil, errors.New("decode length cannot be less than 0")
|
||||
}
|
||||
|
||||
if length > 0x1000000 {
|
||||
return nil, errors.New("decode length cannot be greater than 0x1000000")
|
||||
}
|
||||
|
||||
var data []byte
|
||||
|
||||
// 剩余读长长度
|
||||
unread := int(PakHeadLen + length)
|
||||
readLen := 0
|
||||
for {
|
||||
if unread > 0x1000 {
|
||||
readLen = 0x1000
|
||||
} else {
|
||||
readLen = unread
|
||||
}
|
||||
|
||||
_, err = reader.Peek(readLen) // 先窥探数据
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Buffered返回缓冲中现有的可读取的字节数。
|
||||
if reader.Buffered() < readLen {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 读取
|
||||
buf := make([]byte, readLen)
|
||||
_, err = reader.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = append(data, buf...)
|
||||
|
||||
unread -= readLen
|
||||
//fmt.Println("readLen", readLen)
|
||||
if unread <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(data) < int(PakHeadLen) {
|
||||
return nil, nil
|
||||
}
|
||||
ieee := crc32.NewIEEE()
|
||||
ieee.Write(data[PakHeadLen:])
|
||||
if crc != ieee.Sum32() {
|
||||
return nil, errors.New("crc check error")
|
||||
}
|
||||
return data[PakHeadLen:], nil
|
||||
}
|
||||
Reference in New Issue
Block a user