Golang標(biāo)準(zhǔn)庫binary詳解
Golang標(biāo)準(zhǔn)庫binary
binary包實現(xiàn)了數(shù)字和字節(jié)序列之間的簡單轉(zhuǎn)換。
1、ByteOrder
ByteOrder指定了如何將一個字節(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是一個接口,在binary中有兩個實現(xiàn)了該接口的結(jié)構(gòu)體,分別是littleEndian和bigEndian,也就是小端和大端。大端小端指的是數(shù)據(jù)如何存儲在內(nèi)存中,比如:將低位字節(jié)存儲在低地址空間中、高位字節(jié)存儲在高地址空間中就是小端字節(jié)序;相反,將低位字節(jié)存儲在高地址空間中、高位字節(jié)存儲在低地址空間中就是大端字節(jié)序。
例如:十六進制數(shù)0X12345678以小端和大端字節(jié)序分別在內(nèi)存中的存儲方式如下:

littleEndian:
littleEndian在其它包中是無法創(chuàng)建的,但是在binary中已經(jīng)創(chuàng)建了一個名為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這個方法,在這里是小端字節(jié)序,因此低字節(jié)存儲在低地址空間中,隨著切片的索引的增大,地址空間也是增大的,所以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方法從一個reader中讀取數(shù)據(jù)到data中,data必須是一個指針或一個固定大小的值或切片:
該方法也可以將reader中讀取的數(shù)據(jù)賦值給結(jié)構(gòu)體的各個字段中。
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ù)的二進制寫入一個Writer中,data必須為一個固定值的值或者切片或指向該類數(shù)據(jù)的一個指針:
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ù)時,常常會遇到粘包的現(xiàn)象,因此為了解決粘包我們需要告訴對方我們發(fā)送的數(shù)據(jù)包的大小。一般是使用TLV類型的數(shù)據(jù)協(xié)議,分別是Type、Len、Value,Type和Len為數(shù)據(jù)頭,可以將這個兩個字段都固定為四個字節(jié)。讀取數(shù)據(jù)時,先將Type和Len讀取出來,然后再根據(jù)Len來讀取剩余的數(shù)據(jù):
例如我們使用客戶端向一個服務(wù)端發(fā)送數(shù)據(jù):
client:
package main
import (
"bytes"
"encoding/binary"
"fmt"
"net"
)
// 對數(shù)據(jù)進行編碼
func Encode(id uint32, msg []byte) []byte {
var dataLen uint32 = uint32(len(msg))
// *Buffer實現(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()
}
// 運行結(jié)果:
Receive Data, Type:1, Len:12, Message:hello,world!
Connection has been closed by clientserver:
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個字節(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
}
// 啟動一個協(xié)程處理客戶端
go DealConn(conn)
}
}運行結(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)文章
go語言實現(xiàn)一個簡單的http客戶端抓取遠(yuǎn)程url的方法
這篇文章主要介紹了go語言實現(xiàn)一個簡單的http客戶端抓取遠(yuǎn)程url的方法,實例分析了Go語言http操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-03-03
Go語言中實現(xiàn)Unix風(fēng)格的進程管道方法實例
這篇文章主要為大家介紹了Go語言中實現(xiàn)Unix風(fēng)格的進程管道方法實例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12

