欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang標(biāo)準(zhǔn)庫binary詳解

 更新時(shí)間:2023年05月20日 08:50:34   作者:CoreDump丶  
這篇文章主要介紹了Golang標(biāo)準(zhǔn)庫binary的相關(guān)資料,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

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ī)制

    一文帶你了解Golang中強(qiáng)大的重試機(jī)制

    在 Go 語言中,處理瞬態(tài)錯(cuò)誤是常見的挑戰(zhàn),這些錯(cuò)誤可能會(huì)在一段時(shí)間后自動(dòng)恢復(fù),因此,重試機(jī)制在這些情況下非常重要,所以本文就來和大家聊聊Golang中強(qiáng)大的重試機(jī)制吧
    2025-01-01
  • go語言實(shí)現(xiàn)依賴注入的示例代碼

    go語言實(shí)現(xiàn)依賴注入的示例代碼

    依賴注入和控制反轉(zhuǎn)恰恰相反,它是一種具體的編碼技巧,我們不通過 new 的方式在類內(nèi)部創(chuàng)建依賴類的對象,而是將依賴的類對象在外部創(chuàng)建好之后,通過構(gòu)造函數(shù)、函數(shù)參數(shù)等方式傳遞給類來使用,本文將給大家介紹go語言實(shí)現(xiàn)依賴注入,需要的朋友可以參考下
    2024-01-01
  • GO中使用谷歌GEMINI模型任務(wù)代碼實(shí)例

    GO中使用谷歌GEMINI模型任務(wù)代碼實(shí)例

    這篇文章主要為大家介紹了GO中使用谷歌GEMINI模型任務(wù)代碼實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Go gRPC超時(shí)控制Deadlines用法詳解

    Go gRPC超時(shí)控制Deadlines用法詳解

    這篇文章主要為大家介紹了Go gRPC超時(shí)控制Deadlines用法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • go語言實(shí)現(xiàn)一個(gè)簡單的http客戶端抓取遠(yuǎn)程url的方法

    go語言實(shí)現(xiàn)一個(gè)簡單的http客戶端抓取遠(yuǎn)程url的方法

    這篇文章主要介紹了go語言實(shí)現(xiàn)一個(gè)簡單的http客戶端抓取遠(yuǎn)程url的方法,實(shí)例分析了Go語言http操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-03-03
  • Go模板后端渲染時(shí)vue單頁面沖突

    Go模板后端渲染時(shí)vue單頁面沖突

    go后端模版語法是通過 {{}} ,vue也是通過雙花括號來渲染的,如果使用go渲染vue的html頁面的時(shí)候就會(huì)報(bào)錯(cuò),本文主要介紹了Go模板后端渲染時(shí)vue單頁面沖突,感興趣的可以了解一下
    2024-01-01
  • Go語言中實(shí)現(xiàn)Unix風(fēng)格的進(jìn)程管道方法實(shí)例

    Go語言中實(shí)現(xiàn)Unix風(fēng)格的進(jìn)程管道方法實(shí)例

    這篇文章主要為大家介紹了Go語言中實(shí)現(xiàn)Unix風(fēng)格的進(jìn)程管道方法實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Go語言面試題之select和channel的用法

    Go語言面試題之select和channel的用法

    金九銀十面試季到了(PS:貌似今年一年都是面試季),就業(yè)環(huán)境很差,導(dǎo)致從業(yè)人員不得不卷。本文將重點(diǎn)講解一下Go面試進(jìn)階知識(shí)點(diǎn)之select和channel,需要的可以參考一下
    2022-09-09
  • go判斷文件夾是否存在并創(chuàng)建的實(shí)例

    go判斷文件夾是否存在并創(chuàng)建的實(shí)例

    這篇文章主要介紹了go判斷文件夾是否存在,并創(chuàng)建的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言格式化動(dòng)詞使用詳解

    Go語言格式化動(dòng)詞使用詳解

    這篇文章主要介紹了Go語言格式化動(dòng)詞使用詳解的相關(guān)資料,需要的朋友可以參考下
    2023-08-08

最新評論