golang 编程笔记

【golang】利用字节序binary.BigEndian,避免

2020-04-14  本文已影响0人  dongzd

首先,关于字节序的大端与小端的意思,此处不再解释,可以执行百度。
在项目中遇到一个需求,就是将同一结构类型的消息持久化到文件中,这里就遇到一个问题,把结构消息写入文件,在读取怎么避免读取的消息粘包,导致消息解析错误。
这里我就用到二进制的字节序,每次写消息将消息体的长度也写入消息前面,每次读取文件时候,先将字节序存放的长度读取出来,再读取到buffer里面

写入文件

func writeToFile() {
    data := []byte("你好,江苏!")
    f, er := os.OpenFile("test.dat", os.O_RDWR|os.O_CREATE, 0600)

    checkError(er)
    defer f.Close()

    var err error
    var buf bytes.Buffer
        //写入消息体长度
    err = binary.Write(&buf, binary.BigEndian, int32(len(data)))
    checkError(err)

    _, err = buf.Write(data)
    checkError(err)

    _, err = f.Write(buf.Bytes())
    checkError(err)
}

读取文件

func readFromFile() {
    f, err := os.OpenFile("test.dat", os.O_RDONLY, 0600)
    checkError(err)
    defer f.Close()
    // 建立缓冲读取流,防止数据过大导致内存溢出
    reader := bufio.NewReader(f)

    var msgSize int32
    var er error
    //解码大端编码获取数据体长度
    er = binary.Read(reader, binary.BigEndian, &msgSize)
    checkError(er)
    readBuf := make([]byte, msgSize)
    _, er = io.ReadFull(reader, readBuf)
    checkError(er)

    fmt.Println(string(readBuf))

}

如果多次读取,只有每次打开文件,把文件读取指针偏移到上次读取的位置就可以

Seek(readPos, 0)

拓展

在tcp数据交互中,我们除了可以指定读取的分割符号('\n')办法解决数据粘包,我们也可以使用字节序指定消息体长度来读取数据

发送消息前面先发送长度

func main() {
    conn, err := net.DialTimeout("tcp", "127.0.0.1:8001", time.Second)
    if err != nil {
        panic(err)
    }
    defer conn.Close()
    data := []byte("你好,苏州")
    errw := binary.Write(conn, binary.BigEndian, int32(len(data)))
    if errw != nil {
        return
    }
    conn.Write(data)
    // conn.Write([]byte("你好"))
    // conn.Write([]byte("中国\n"))
    // conn.Write([]byte("\n"))
}

解析消息体长度

func handle(conn net.Conn){
    defer func ()  {
        fmt.Println("客户端断开")
        conn.Close()
    }()
    reader := bufio.NewReader(conn)

    for {
        var err error
        var bodyLen int32
        // 逻辑每次读取获取数据长度,读取完指定数据长度数据,下次循环,则读取下次数据体长度
        err = binary.Read(reader, binary.BigEndian, &bodyLen)
        if err != nil {
            fmt.Println(err)
            return
        }
    
        body := make([]byte, bodyLen)
        _, err = io.ReadFull(reader, body)
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(string(body))
    }

}
上一篇下一篇

猜你喜欢

热点阅读