binary 序列化
最后更新于:2022-04-02 02:43:22
[TOC]
## binary.Size 获取结构体的大小
固定大小的结构体,就要求结构体中不能出现\[\]byte这样的切片成员,否则Size返回-1,且不能进行正常的序列化操作
code
```
a0 := 123 // or a0 := "123"
fmt.Printf("a0 : %d \n", binary.Size(a0)) // -1
a1 := int16(123)
fmt.Printf("a1 : %d \n", binary.Size(a1)) // 2
a2 := uint16(123)
fmt.Printf("a2 : %d \n", binary.Size(a2)) // 2
a3 := uint32(123)
fmt.Printf("a3 : %d \n", binary.Size(a3)) // 4
a4 := uint64(123)
fmt.Printf("a4 : %d \n", binary.Size(a4)) // 8
type b struct {
b1 uint16
b2 uint32
}
var B b
fmt.Printf("B : %d \n", binary.Size(B)) //6
B.b1 = uint16(32)
fmt.Printf("B : %d \n", binary.Size(B)) //6
```
## 小端序与大端序 序列化
1. 网络的数据是以大端数据模式进行交互
2. 主机大多数以小端模式处理,如果不转换,数据会混乱
数值`0x2211`使用两个字节储存:高位字节是`0x22`,低位字节是`0x11`,**并不是数值的大小**
* **大端字节序**:高位字节在前,低位字节在后,这是人类读写数值的方法。
* **小端字节序**:低位字节在前,高位字节在后,即以`0x1122`形式储存。
```
a :=int32(65535)
buf :=new(bytes.Buffer)
binary.Write(buf,binary.LittleEndian,a)
fmt.Printf("%+v\n", buf.Bytes()) // [255 255 0 0]
buf2 :=new(bytes.Buffer)
binary.Write(buf2,binary.BigEndian,a)
fmt.Printf("%+v\n", buf2.Bytes()) // [0 0 255 255]
```
> 结构体也可同理实现小端序,大端序的序列化
### 结构体转为序列化
```
type A struct {
One int32
Two int32
}
var a A
a.One = int32(1)
a.Two = int32(3)
fmt.Println("a's size is ", binary.Size(a)) // 8
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, a)
fmt.Println("after write ,buf is:", buf.Bytes()) //[1 0 0 0 3 0 0 0]
```
1. 这里采用了小端序的方式进行序列化(x86架构都是小端序,网络字节序是大端序)
2. 对于结构体中得“_”成员不进行序列化
3. 结构体中不能出现\[\]byte这样的切片成员,否则Size返回-1
### 序列化(buffer)转结构体
```
type A struct {
One uint16
Two int32
}
var aa A
aa.One=65535
var bb A
buf := new(bytes.Buffer)
binary.Write(buf,binary.LittleEndian,aa) //序列化
binary.Read(buf,binary.LittleEndian,&bb) //反序列化
fmt.Printf("%+v\n", bb) //{One:65535 Two:0}
```
> 只有多于一位的才需要指定大端序
### 编码字节切片
```
// write
v := uint32(500)
buf := make([]byte,4) //需大于4位, 过小 抛出异常 index out of range [3] with length 3
binary.BigEndian.PutUint32(buf, v)
// read
x := binary.BigEndian.Uint32(buf)
fmt.Printf("%+v\n", x)
```
### 流处理
读写固定长度值的流 (stream)
Read 通过指定类型的字节序把字节解码 (decode) 到 data 变量中。解码布尔类型时,0 字节 (也就是 `[]byte{0x00}` ) 为 false, 其他都为 true
Read
```
func main() {
var(
piVar float64
boolVar bool
)
piByte := []byte{0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40}
boolByte := []byte{0x00}
piBuffer := bytes.NewReader(piByte)
boolBuffer := bytes.NewReader(boolByte)
binary.Read(piBuffer, binary.LittleEndian, &piVar)
binary.Read(boolBuffer, binary.LittleEndian, & boolVar)
fmt.Println("pi", piVar) // pi 3.141592653589793
fmt.Println("bool", boolVar) // bool false
}
```
Write
```
func main() {
buf := new(bytes.Buffer)
var pi float64 = math.Pi
err := binary.Write(buf, binary.LittleEndian, pi)
if err != nil {
fmt.Println("binary.Write failed:", err)
}
fmt.Printf("% x", buf.Bytes()) // 18 2d 44 54 fb 21 09 40
}
```
在实际编码中,面对复杂的数据结构,可考虑使用更标准化高效的协议,比如[google-Protocol Buffer](https://developers.google.com/protocol-buffers/)
### 包头定义的例子
```
// 定义协议
type Pack struct {
Length uint32 // 长度 4个字节
Order uint32 // 序号
SecretKey1 byte // 随机秘钥1
SecretKey2 byte // 随机秘钥2
Cmd constant.CmdCode // cmd 命令 如 0x0008=为登录 0x0008=登出
StaticCode constant.StaticCode // 状态 0-正常 1-出错 2-命令未完成 0x000 3-命令需要ack
Encryption constant.EncrCode // 加密: 0-误操作 1-AES256 2-国产sm4
Reserv uint16 // 随机值
Body []byte // body
Ext Ext
}
// Pack 的值赋值给 writer
func (p *Pack) Pack(writer io.Writer) error {
var err error
err = binary.Write(writer, binary.BigEndian, &p.Length)
err = binary.Write(writer, binary.BigEndian, &p.Order)
err = binary.Write(writer, binary.BigEndian, &p.SecretKey1)
err = binary.Write(writer, binary.BigEndian, &p.SecretKey2)
err = binary.Write(writer, binary.BigEndian, &p.Cmd)
err = binary.Write(writer, binary.BigEndian, &p.StaticCode)
err = binary.Write(writer, binary.BigEndian, &p.Encryption)
err = binary.Write(writer, binary.BigEndian, &p.Reserv)
err = binary.Write(writer, binary.BigEndian, &p.Body)
return err
}
// pack的值复制给 reader
func (p *Pack) UnPack(b []byte) error {
reader := bytes.NewReader(b)
var err error
err = binary.Read(reader, binary.BigEndian, &p.Length)
err = binary.Read(reader, binary.BigEndian, &p.Order)
err = binary.Read(reader, binary.BigEndian, &p.SecretKey1)
err = binary.Read(reader, binary.BigEndian, &p.SecretKey2)
err = binary.Read(reader, binary.BigEndian, &p.Cmd)
err = binary.Read(reader, binary.BigEndian, &p.StaticCode)
err = binary.Read(reader, binary.BigEndian, &p.Encryption)
err = binary.Read(reader, binary.BigEndian, &p.Reserv)
p.Body = make([]byte, p.Length-HAND_BODY)
err = binary.Read(reader, binary.BigEndian, &p.Body)
return err
}
```
';