注意到 encoding/gob 包是因为看到 net/rpc 包使用它编解码。

二者都是标准库下的包。

一、示例代码和执行结果

// hello.go
package main

import (
    "bytes"
    "encoding/gob"
    "fmt"
)

type Request struct {
    ServiceMethod string   // format: "Service.Method"
    Seq           uint64   // sequence number chosen by client
    next          *Request // for free list in Server
}

type Request3 struct {
    ServiceMethod string
}

type Request4 struct {
    Other int64
}

func main() {
    var err error
    a := &bytes.Buffer{}
    encoder := gob.NewEncoder(a)
    _ = encoder.Encode(&Request{
        Seq:           23,
        ServiceMethod: "firstMethod",
    })
    _ = encoder.Encode(&Request{
        Seq:           9,
        ServiceMethod: "secondMethod",
    })
    _ = encoder.Encode(&Request{
        Seq:           16,
        ServiceMethod: "thirdMethod",
    })
    _ = encoder.Encode(&Request{
        Seq:           77,
        ServiceMethod: "fourthMethod",
    })
    fmt.Printf("buf: %#v\n\n", a)

    b := bytes.NewBufferString(a.String())
    decoder := gob.NewDecoder(b)
    ans1 := &Request{}
    _ = decoder.Decode(ans1)
    fmt.Println("ans1 err: ", err)
    fmt.Printf("ans1: %#v\n\n", ans1)

    ans2 := &Request{}
    err = decoder.Decode(ans2)
    fmt.Println("ans2 err: ", err)
    fmt.Printf("ans2: %#v\n\n", ans2)

    ans3 := &Request3{}
    err = decoder.Decode(ans3)
    fmt.Println("ans3 err: ", err)
    fmt.Printf("ans3: %#v\n\n", ans3)

    ans4 := &Request4{}
    err = decoder.Decode(ans4)
    fmt.Println("ans4 err: ", err)
    fmt.Printf("ans4: %#v\n\n", ans4)

    ans5 := &Request{}
    err = decoder.Decode(ans5)
    fmt.Println("ans5 err: ", err)
    fmt.Printf("ans5: %#v\n\n", ans5)
}

$ go run ./hello.go 
buf: &bytes.Buffer{buf:[]uint8{0x2f, 0xff, 0x81, 0x3, 0x1, 0x1, 0x7, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1, 0xff, 0x82, 0x0, 0x1, 0x2, 0x1, 0xd, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x1, 0xc, 0x0, 0x1, 0x3, 0x53, 0x65, 0x71, 0x1, 0x6, 0x0, 0x0, 0x0, 0x12, 0xff, 0x82, 0x1, 0xb, 0x66, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x1, 0x17, 0x0, 0x13, 0xff, 0x82, 0x1, 0xc, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x1, 0x9, 0x0, 0x12, 0xff, 0x82, 0x1, 0xb, 0x74, 0x68, 0x69, 0x72, 0x64, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x1, 0x10, 0x0, 0x13, 0xff, 0x82, 0x1, 0xc, 0x66, 0x6f, 0x75, 0x72, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x1, 0x4d, 0x0}, off:0, lastRead:0}

ans1 err:  <nil>
ans1: &main.Request{ServiceMethod:"firstMethod", Seq:0x17, next:(*main.Request)(nil)}

ans2 err:  <nil>
ans2: &main.Request{ServiceMethod:"secondMethod", Seq:0x9, next:(*main.Request)(nil)}

ans3 err:  <nil>
ans3: &main.Request3{ServiceMethod:"thirdMethod"}

ans4 err:  gob: type mismatch: no fields matched compiling decoder for Request4
ans4: &main.Request4{Other:0}

ans5 err:  EOF
ans5: &main.Request{ServiceMethod:"", Seq:0x0, next:(*main.Request)(nil)}

二、解读

以上代码一次性编码写入 4 条数据,分 5 次读取、解码。

  1. ans1、ans2 能正确解读,说明 gob 能准确识别边界;
  2. ans3 能正确解读,gob 解码的时候不在乎结构具体叫什么,但在乎其中的具体字段;
  3. ans4 解读提示 type mismatch,说明想要准确解读需要数据写入方、读取方彼此协调好,使用相同的数据结构;
  4. ans5 传入的数据结构能对应上第 4 条输入,但没能解读到数据,提示 EOF,说明 ans4 虽然没能准确解读,但使用了 gob 解码需要的信息。

综合以上,可以归纳 gob 使用的特点
1、可先不读取解码,一次性多条编码写入;
2、读取解码时,可自动识别每一条信息的边界;
3、每次读取解码时,先取出一条编码信息,再同传入的指定结构匹配并填充,失败时数据不会放回;
4、解码时发现没有数据,报错 EOF

另:编、解码传入的指定结构都需要是指针类型。


夜游神
637 声望581 粉丝

哈哈。