最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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ù)如何存儲在內(nèi)存中,比如:將低位字節(jié)存儲在低地址空間中、高位字節(jié)存儲在高地址空間中就是小端字節(jié)序;相反,將低位字節(jié)存儲在高地址空間中、高位字節(jié)存儲在低地址空間中就是大端字節(jié)序。

例如:十六進(jìn)制數(shù)0X12345678以小端和大端字節(jié)序分別在內(nèi)存中的存儲方式如下:

在這里插入圖片描述

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é)存儲在低地址空間中,隨著切片的索引的增大,地址空間也是增大的,所以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í),常常會遇到粘包的現(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
		}
		// 啟動一個(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)文章

  • 學(xué)會提升Go語言編碼效率技巧拒絕加班!

    學(xué)會提升Go語言編碼效率技巧拒絕加班!

    這篇文章主要為大家介紹了Go語言編碼效率提升技巧詳解,學(xué)會了從此拒絕加班,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • golang WebDav 多目錄文件服務(wù)的實(shí)現(xiàn)

    golang WebDav 多目錄文件服務(wù)的實(shí)現(xiàn)

    本文介紹使用Golang實(shí)現(xiàn)一個(gè)WebDav服務(wù),支持在一個(gè)端口下為多個(gè)用戶提供獨(dú)立的目錄訪問權(quán)限,通過示例代碼介紹了配置多個(gè)目錄,并實(shí)現(xiàn)基本的用戶認(rèn)證,感興趣的可以了解一下
    2025-12-12
  • go語言中iota和左移右移的使用說明

    go語言中iota和左移右移的使用說明

    這篇文章主要介紹了go語言中iota和左移右移的使用說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Gin框架中的PostForm用法及說明

    Gin框架中的PostForm用法及說明

    這篇文章主要介紹了Gin框架中的PostForm用法及說明,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Golang使用Token來驗(yàn)證

    Golang使用Token來驗(yàn)證

    token指的是一種用于驗(yàn)證用戶身份或授權(quán)訪問的憑證,本文主要介紹了Golang使用Token來驗(yàn)證,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-08-08
  • Go語言編程通過dwarf獲取內(nèi)聯(lián)函數(shù)

    Go語言編程通過dwarf獲取內(nèi)聯(lián)函數(shù)

    這篇文章主要為大家介紹了Go語言編程通過dwarf獲取內(nèi)聯(lián)函數(shù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • golang生成RSA公鑰和密鑰的實(shí)現(xiàn)方法

    golang生成RSA公鑰和密鑰的實(shí)現(xiàn)方法

    本文主要介紹了golang生成RSA公鑰和密鑰的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • 基于go+vue實(shí)現(xiàn)的golang每日新聞數(shù)據(jù)瀏覽與檢索平臺(推薦)

    基于go+vue實(shí)現(xiàn)的golang每日新聞數(shù)據(jù)瀏覽與檢索平臺(推薦)

    gonews是基于 go+vue 實(shí)現(xiàn)的golang每日新聞瀏覽與檢索平臺,本文通過實(shí)例代碼給大家講解,介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友參考下吧
    2018-01-01
  • IdeaGo啟動報(bào)錯(cuò)Failed to create JVM的問題解析

    IdeaGo啟動報(bào)錯(cuò)Failed to create JVM的問題解析

    這篇文章主要介紹了IdeaGo啟動報(bào)錯(cuò)Failed to create JVM的問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • go?mod文件內(nèi)容版本號簡單用法詳解

    go?mod文件內(nèi)容版本號簡單用法詳解

    這篇文章主要為大家介紹了go?mod文件內(nèi)容版本號簡單用法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10

最新評論

瓮安县| 大关县| 湾仔区| 台中县| 阜新市| 三门县| 唐海县| 秦皇岛市| 宜宾县| 勐海县| 乌海市| 额尔古纳市| 兰西县| 上林县| 南华县| 泸溪县| 彭州市| 石河子市| 禄丰县| 福建省| 桦南县| 双鸭山市| 七台河市| 白朗县| 南丹县| 星子县| 大石桥市| 曲松县| 宜春市| 元谋县| 洮南市| 盐池县| 齐齐哈尔市| 呼伦贝尔市| 巴青县| 漾濞| 通许县| 巴彦淖尔市| 左权县| 濉溪县| 斗六市|