Golang標(biāo)準(zhǔn)庫binary詳解
Golang標(biāo)準(zhǔn)庫binary
binary包實(shí)現(xiàn)了數(shù)字和字節(jié)序列之間的簡單轉(zhuǎn)換。
1、ByteOrder
ByteOrder指定了如何將一個(gè)字節(jié)序列轉(zhuǎn)換為16、32或64位的無符號整數(shù):
type ByteOrder interface { Uint16([]byte) uint16 Uint32([]byte) uint32 Uint64([]byte) uint64 PutUint16([]byte, uint16) PutUint32([]byte, uint32) PutUint64([]byte, uint64) String() string }
ByteOrder是一個(gè)接口,在binary中有兩個(gè)實(shí)現(xiàn)了該接口的結(jié)構(gòu)體,分別是littleEndian和bigEndian,也就是小端和大端。大端小端指的是數(shù)據(jù)如何存儲(chǔ)在內(nèi)存中,比如:將低位字節(jié)存儲(chǔ)在低地址空間中、高位字節(jié)存儲(chǔ)在高地址空間中就是小端字節(jié)序;相反,將低位字節(jié)存儲(chǔ)在高地址空間中、高位字節(jié)存儲(chǔ)在低地址空間中就是大端字節(jié)序。
例如:十六進(jìn)制數(shù)0X12345678以小端和大端字節(jié)序分別在內(nèi)存中的存儲(chǔ)方式如下:
littleEndian:
littleEndian在其它包中是無法創(chuàng)建的,但是在binary中已經(jīng)創(chuàng)建了一個(gè)名為LittleEndian的該結(jié)構(gòu)體,我們可以直接使用。
var LittleEndian littleEndian type littleEndian struct{} func (littleEndian) Uint16(b []byte) uint16 { _ = b[1] // 編譯器的邊界檢測提示 return uint16(b[0]) | uint16(b[1])<<8 } func (littleEndian) PutUint16(b []byte, v uint16) { _ = b[1] // early bounds check to guarantee safety of writes below b[0] = byte(v) b[1] = byte(v >> 8) } func (littleEndian) Uint32(b []byte) uint32 { _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 } func (littleEndian) PutUint32(b []byte, v uint32) { _ = b[3] // early bounds check to guarantee safety of writes below b[0] = byte(v) b[1] = byte(v >> 8) b[2] = byte(v >> 16) b[3] = byte(v >> 24) } func (littleEndian) Uint64(b []byte) uint64 { _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 } func (littleEndian) PutUint64(b []byte, v uint64) { _ = b[7] // early bounds check to guarantee safety of writes below b[0] = byte(v) b[1] = byte(v >> 8) b[2] = byte(v >> 16) b[3] = byte(v >> 24) b[4] = byte(v >> 32) b[5] = byte(v >> 40) b[6] = byte(v >> 48) b[7] = byte(v >> 56) } func (littleEndian) String() string { return "LittleEndian" } func (littleEndian) GoString() string { return "binary.LittleEndian" }
在上面定義的方法也比較簡單,就是字節(jié)序列與無符號數(shù)之間的轉(zhuǎn)換。例如Uint16這個(gè)方法,在這里是小端字節(jié)序,因此低字節(jié)存儲(chǔ)在低地址空間中,隨著切片的索引的增大,地址空間也是增大的,所以b[1]所在空間是高地址,因此將b[1]左移八位后與b[0]位與就可以得到uint16類型的數(shù)據(jù)了。
bigEndian:
大端與小端相反:
var BigEndian bigEndian type bigEndian struct{} func (bigEndian) Uint16(b []byte) uint16 { _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 return uint16(b[1]) | uint16(b[0])<<8 } func (bigEndian) PutUint16(b []byte, v uint16) { _ = b[1] // early bounds check to guarantee safety of writes below b[0] = byte(v >> 8) b[1] = byte(v) } func (bigEndian) Uint32(b []byte) uint32 { _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 } func (bigEndian) PutUint32(b []byte, v uint32) { _ = b[3] // early bounds check to guarantee safety of writes below b[0] = byte(v >> 24) b[1] = byte(v >> 16) b[2] = byte(v >> 8) b[3] = byte(v) } func (bigEndian) Uint64(b []byte) uint64 { _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 } func (bigEndian) PutUint64(b []byte, v uint64) { _ = b[7] // early bounds check to guarantee safety of writes below b[0] = byte(v >> 56) b[1] = byte(v >> 48) b[2] = byte(v >> 40) b[3] = byte(v >> 32) b[4] = byte(v >> 24) b[5] = byte(v >> 16) b[6] = byte(v >> 8) b[7] = byte(v) } func (bigEndian) String() string { return "BigEndian" } func (bigEndian) GoString() string { return "binary.BigEndian" }
2、binary.Read
Read方法從一個(gè)reader中讀取數(shù)據(jù)到data中,data必須是一個(gè)指針或一個(gè)固定大小的值或切片:
該方法也可以將reader中讀取的數(shù)據(jù)賦值給結(jié)構(gòu)體的各個(gè)字段中。
func Read(r io.Reader, order ByteOrder, data interface{}) error { // Fast path for basic types and slices. if n := intDataSize(data); n != 0 { bs := make([]byte, n) if _, err := io.ReadFull(r, bs); err != nil { return err } switch data := data.(type) { case *bool: *data = bs[0] != 0 case *int8: *data = int8(bs[0]) case *uint8: *data = bs[0] case *int16: *data = int16(order.Uint16(bs)) case *uint16: *data = order.Uint16(bs) case *int32: *data = int32(order.Uint32(bs)) case *uint32: *data = order.Uint32(bs) case *int64: *data = int64(order.Uint64(bs)) case *uint64: *data = order.Uint64(bs) case *float32: *data = math.Float32frombits(order.Uint32(bs)) case *float64: *data = math.Float64frombits(order.Uint64(bs)) case []bool: for i, x := range bs { // Easier to loop over the input for 8-bit values. data[i] = x != 0 } case []int8: for i, x := range bs { data[i] = int8(x) } case []uint8: copy(data, bs) case []int16: for i := range data { data[i] = int16(order.Uint16(bs[2*i:])) } case []uint16: for i := range data { data[i] = order.Uint16(bs[2*i:]) } case []int32: for i := range data { data[i] = int32(order.Uint32(bs[4*i:])) } case []uint32: for i := range data { data[i] = order.Uint32(bs[4*i:]) } case []int64: for i := range data { data[i] = int64(order.Uint64(bs[8*i:])) } case []uint64: for i := range data { data[i] = order.Uint64(bs[8*i:]) } case []float32: for i := range data { data[i] = math.Float32frombits(order.Uint32(bs[4*i:])) } case []float64: for i := range data { data[i] = math.Float64frombits(order.Uint64(bs[8*i:])) } default: n = 0 // fast path doesn't apply } if n != 0 { return nil } } // Fallback to reflect-based decoding. v := reflect.ValueOf(data) size := -1 switch v.Kind() { case reflect.Ptr: v = v.Elem() size = dataSize(v) case reflect.Slice: size = dataSize(v) } if size < 0 { return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String()) } d := &decoder{order: order, buf: make([]byte, size)} if _, err := io.ReadFull(r, d.buf); err != nil { return err } d.value(v) return nil }
3、binary.Write
Write方法將數(shù)據(jù)的二進(jìn)制寫入一個(gè)Writer中,data必須為一個(gè)固定值的值或者切片或指向該類數(shù)據(jù)的一個(gè)指針:
func Write(w io.Writer, order ByteOrder, data interface{}) error { // Fast path for basic types and slices. if n := intDataSize(data); n != 0 { bs := make([]byte, n) switch v := data.(type) { case *bool: if *v { bs[0] = 1 } else { bs[0] = 0 } case bool: if v { bs[0] = 1 } else { bs[0] = 0 } case []bool: for i, x := range v { if x { bs[i] = 1 } else { bs[i] = 0 } } case *int8: bs[0] = byte(*v) case int8: bs[0] = byte(v) case []int8: for i, x := range v { bs[i] = byte(x) } case *uint8: bs[0] = *v case uint8: bs[0] = v case []uint8: bs = v case *int16: order.PutUint16(bs, uint16(*v)) case int16: order.PutUint16(bs, uint16(v)) case []int16: for i, x := range v { order.PutUint16(bs[2*i:], uint16(x)) } case *uint16: order.PutUint16(bs, *v) case uint16: order.PutUint16(bs, v) case []uint16: for i, x := range v { order.PutUint16(bs[2*i:], x) } case *int32: order.PutUint32(bs, uint32(*v)) case int32: order.PutUint32(bs, uint32(v)) case []int32: for i, x := range v { order.PutUint32(bs[4*i:], uint32(x)) } case *uint32: order.PutUint32(bs, *v) case uint32: order.PutUint32(bs, v) case []uint32: for i, x := range v { order.PutUint32(bs[4*i:], x) } case *int64: order.PutUint64(bs, uint64(*v)) case int64: order.PutUint64(bs, uint64(v)) case []int64: for i, x := range v { order.PutUint64(bs[8*i:], uint64(x)) } case *uint64: order.PutUint64(bs, *v) case uint64: order.PutUint64(bs, v) case []uint64: for i, x := range v { order.PutUint64(bs[8*i:], x) } case *float32: order.PutUint32(bs, math.Float32bits(*v)) case float32: order.PutUint32(bs, math.Float32bits(v)) case []float32: for i, x := range v { order.PutUint32(bs[4*i:], math.Float32bits(x)) } case *float64: order.PutUint64(bs, math.Float64bits(*v)) case float64: order.PutUint64(bs, math.Float64bits(v)) case []float64: for i, x := range v { order.PutUint64(bs[8*i:], math.Float64bits(x)) } } _, err := w.Write(bs) return err } // Fallback to reflect-based encoding. v := reflect.Indirect(reflect.ValueOf(data)) size := dataSize(v) if size < 0 { return errors.New("binary.Write: invalid type " + reflect.TypeOf(data).String()) } buf := make([]byte, size) e := &encoder{order: order, buf: buf} e.value(v) _, err := w.Write(buf) return err }
4、binary.Read和binary.Write的應(yīng)用
當(dāng)我們使用tcp傳輸數(shù)據(jù)時(shí),常常會(huì)遇到粘包的現(xiàn)象,因此為了解決粘包我們需要告訴對方我們發(fā)送的數(shù)據(jù)包的大小。一般是使用TLV類型的數(shù)據(jù)協(xié)議,分別是Type、Len、Value,Type和Len為數(shù)據(jù)頭,可以將這個(gè)兩個(gè)字段都固定為四個(gè)字節(jié)。讀取數(shù)據(jù)時(shí),先將Type和Len讀取出來,然后再根據(jù)Len來讀取剩余的數(shù)據(jù):
例如我們使用客戶端向一個(gè)服務(wù)端發(fā)送數(shù)據(jù):
client:
package main import ( "bytes" "encoding/binary" "fmt" "net" ) // 對數(shù)據(jù)進(jìn)行編碼 func Encode(id uint32, msg []byte) []byte { var dataLen uint32 = uint32(len(msg)) // *Buffer實(shí)現(xiàn)了Writer buffer := bytes.NewBuffer([]byte{}) // 將id寫入字節(jié)切片 if err := binary.Write(buffer, binary.LittleEndian, &id); err != nil { fmt.Println("Write to buffer error:", err) } // 將數(shù)據(jù)長度寫入字節(jié)切片 if err := binary.Write(buffer, binary.LittleEndian, &dataLen); err != nil { fmt.Println("Write to buffer error:", err) } // 最后將數(shù)據(jù)添加到后面 msg = append(buffer.Bytes(), msg...) return msg } func main() { dial, err := net.Dial("tcp4", "127.0.0.1:6666") if err != nil { fmt.Println("Dial tcp error:", err) } // 向服務(wù)端發(fā)送hello,world! msg := []byte("hello,world!") var id uint32 = 1 data := Encode(id, msg) dial.Write(data) dial.Close() } // 運(yùn)行結(jié)果: Receive Data, Type:1, Len:12, Message:hello,world! Connection has been closed by client
server:
package main import ( "bytes" "encoding/binary" "fmt" "io" "net" ) // 解碼,從字節(jié)切片中獲取id和len func Decode(encoded []byte) (id uint32, l uint32) { buffer := bytes.NewBuffer(encoded) if err := binary.Read(buffer, binary.LittleEndian, &id); err != nil { fmt.Println("Read from buffer error:", err) } if err := binary.Read(buffer, binary.LittleEndian, &l); err != nil { fmt.Println("Read from buffer error:", err) } return id, l } const MAX_PACKAGE = 4096 func DealConn(conn net.Conn) { defer conn.Close() head := make([]byte, 8) for { // 先讀取8個(gè)字節(jié)的頭部,也就是id和dataLen _, err := io.ReadFull(conn, head) if err != nil { if err == io.EOF { fmt.Println("Connection has been closed by client") } else { fmt.Println("Read error:", err) } return } id, l := Decode(head) if l > MAX_PACKAGE { fmt.Println("Received data grater than MAX_PACKAGE") return } // 然后讀取剩余數(shù)據(jù) data := make([]byte, l) _, err = io.ReadFull(conn, data) if err != nil { if err == io.EOF { fmt.Println("Connection has been closed by client") } else { fmt.Println("Read error:", err) } return } // 打印收到的數(shù)據(jù) fmt.Printf("Receive Data, Type:%d, Len:%d, Message:%s\n", id, l, string(data)) } } func main() { listener, err := net.Listen("tcp", "127.0.0.1:6666") if err != nil { fmt.Println("Listen tcp error:", err) return } for { conn, err := listener.Accept() if err != nil { fmt.Println("Accept error:", err) break } // 啟動(dòng)一個(gè)協(xié)程處理客戶端 go DealConn(conn) } }
運(yùn)行結(jié)果:
Receive Data, Type:1, Len:12, Message:hello,world!
Connection has been closed by client
到此這篇關(guān)于Golang標(biāo)準(zhǔn)庫binary詳解的文章就介紹到這了,更多相關(guān)Golang標(biāo)準(zhǔn)庫binary內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文帶你了解Golang中強(qiáng)大的重試機(jī)制
在 Go 語言中,處理瞬態(tài)錯(cuò)誤是常見的挑戰(zhàn),這些錯(cuò)誤可能會(huì)在一段時(shí)間后自動(dòng)恢復(fù),因此,重試機(jī)制在這些情況下非常重要,所以本文就來和大家聊聊Golang中強(qiáng)大的重試機(jī)制吧2025-01-01GO中使用谷歌GEMINI模型任務(wù)代碼實(shí)例
這篇文章主要為大家介紹了GO中使用谷歌GEMINI模型任務(wù)代碼實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01go語言實(shí)現(xiàn)一個(gè)簡單的http客戶端抓取遠(yuǎn)程url的方法
這篇文章主要介紹了go語言實(shí)現(xiàn)一個(gè)簡單的http客戶端抓取遠(yuǎn)程url的方法,實(shí)例分析了Go語言http操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03Go語言中實(shí)現(xiàn)Unix風(fēng)格的進(jìn)程管道方法實(shí)例
這篇文章主要為大家介紹了Go語言中實(shí)現(xiàn)Unix風(fēng)格的進(jìn)程管道方法實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12go判斷文件夾是否存在并創(chuàng)建的實(shí)例
這篇文章主要介紹了go判斷文件夾是否存在,并創(chuàng)建的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12