Golang中的位元組序列化操作

來源:互聯網
上載者:User

標籤:golang 位元組

在寫網路程式的時候,我們經常需要將結構體或者整數等資料類型序列化成二進位的buffer串。或者從一個buffer中解析出來一個結構體出來,最典型的就是在協議的header部分表徵head length 或者body length在拼包和拆包的過程中,需要按照規定的整數類型進行解析,且涉及到大小端序的問題。

1.C中是怎麼操作的

在C中我們最簡單的方法是用memcpy來一個整形數或者結構體等其他類型複製到一塊記憶體中,然後在強轉回需要的類型。如:

    // produce    int a = 32;    char *buf  = (char *)malloc(sizeof(int));    memcpy(buf,&a,sizeof(int));    // consume    int b ;    memcpy(&b,buf,sizeof(int))

必要的時候採用ntoh/hton系列函數進行大小端序的轉換。

2.golang中操作

通過"encoding/binary"可以提供常用的二進位序列化的功能。該模組主要提供了如下幾個介面:

func Read(r io.Reader, order ByteOrder, data interface{}) errorfunc Write(w io.Writer, order ByteOrder, data interface{}) errorfunc Size(v interface{}) intvar BigEndian bigEndianvar LittleEndian littleEndian/*type ByteOrder interface {Uint16([]byte) uint16Uint32([]byte) uint32Uint64([]byte) uint64PutUint16([]byte, uint16)PutUint32([]byte, uint32)PutUint64([]byte, uint64)String() string}/*

通過Read介面可以將buf中得內容填充到data參數表示的資料結構中,通過Write介面可以將data參數裡麵包含的資料寫入到buffer中。 變數BigEndian和LittleEndian是實現了ByteOrder介面的對象,通過介面中提供的方法可以直接將uintx類型序列化(uintx())或者還原序列化(putuintx())到buf中。

2.1將結構體序列化到一個buf中

在序列化結構對象時,需要注意的是,被序列化的結構的大小必須是已知的,可以通過Size介面來獲得該結構的大小,從而決定buffer的大小。

i := uint16(1)size :=  binary.Size(i)

固定大小的結構體,就要求結構體中不能出現[]byte這樣的切片成員,否則Size返回-1,且不能進行正常的序列化操作。

type A struct {    // should be exported member when read back from buffer    One int32    Two int32}var a Aa.One = int32(1)a.Two = int32(2)buf := new(bytes.Buffer)fmt.Println("a‘s size is ",binary.Size(a))binary.Write(buf,binary.LittleEndian,a)fmt.Println("after write ,buf is:",buf.Bytes())

對應的輸出為:

a‘s size is  8after write ,buf is : [1 0 0 0 2 0 0 0]

通過Size可以得到所需buffer的大小。通過Write可以將對象a的內容序列化到buffer中。這裡採用了小端序的方式進行序列化(x86架構都是小端序,網路位元組序是大端序)。

對於結構體中得“_”成員不進行序列化。

2.2從buf中還原序列化回一個結構

從buffer中讀取時,一樣要求結構體的大小要固定,且需要還原序列化的結構體成員必須是可匯出的也就是必須是大寫開頭的成員,同樣對於“_”不進行還原序列化:

type A struct {    // should be exported member when read back from buffer    One int32    Two int32}var aa Abuf := new(bytes.Buffer)binary.Write(buf,binary.LittleEndian,a)binary.Read(buf,binary.LittleEndian,&aa)fmt.Println("after aa is ",aa)

輸出為:

after write ,bufis : [1 0 0 0 2 0 0 0]before aa is : {0 0}after aa is  {1 2}

這裡使用Read從buffer中將資料匯入到結構體對象aa中。如果結構體中對應的成員不是可匯出的,那麼在轉換的時候會panic出錯。

2.3將整數序列化到buf中,並從buf中還原序列化出來

我們可以通過Read/Write直接去讀或者寫一個uintx類型的變數來實現對整形數的序列化和還原序列化。由於在網路中,對於整形數的序列化非常常用,因此系統庫提供了type ByteOrder介面可以方便的對uint16/uint32/uint64進行序列化和還原序列化:

int16buf := new(bytes.Buffer)i := uint16(1)binary.Write(int16buf,binary.LittleEndian,i)fmt.Println(“write buf is:”int16buf.Bytes())var int16buf2 [2]bytebinary.LittleEndian.PutUint16(int16buf2[:],uint16(1))fmt.Println("put buffer is :",int16buf2[:])ii := binary.LittleEndian.Uint16(int16buf2[:])fmt.Println("Get buf is :",ii)

輸出為:

write buffer is : [1 0]put buf is: [1 0]Get buf is : 1

通過調用binary.LittleEndian.PutUint16,可以按照小端序的格式將uint16類型的資料序列化到buffer中。通過binary.LittleEndian.Uint16將buffer中內容還原序列化出來。

3. 一個實在的例子

我們來看一個網路包包頭的定義和初始化:

type Head struct {    Cmd byte    Version byte    Magic   uint16    Reserve byte    HeadLen byte    BodyLen uint16}func NewHead(buf []byte)*Head{    head := new(Head)    head.Cmd     = buf[0]    head.Version = buf[1]    head.Magic   = binary.BigEndian.Uint16(buf[2:4])    head.Reserve = buf[4]    head.HeadLen = buf[5]    head.BodyLen = binary.BigEndian.Uint16(buf[6:8])    return head}

這個是一個常見的在tcp 拼包得例子。在例子中通過binary.BigEndian.Uint16將資料按照網路序的格式讀出來,放入到head中對應的結構裡面。


本文出自 “Done_in_72_hours” 部落格,請務必保留此出處http://gotaly.blog.51cto.com/8861157/1539119

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.