美文网首页golang 编程笔记
【golang】利用字节序binary.BigEndian,避免

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

作者: dongzd | 来源:发表于2020-04-14 16:43 被阅读0次

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

    相关文章

      网友评论

        本文标题:【golang】利用字节序binary.BigEndian,避免

        本文链接:https://www.haomeiwen.com/subject/yzetvhtx.html