天天看點

Golang中的位元組序列化操作

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

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

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

通過"encoding/binary"可以提供常用的二進制序列化的功能。該子產品主要提供了如下幾個接口:

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

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

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

對應的輸出為:

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

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

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

輸出為:

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

我們可以通過Read/Write直接去讀或者寫一個uintx類型的變量來實作對×××數的序列化和反序列化。由于在網絡中,對于×××數的序列化非常常用,是以系統庫提供了type ByteOrder接口可以友善的對uint16/uint32/uint64進行序列化和反序列化:

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

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

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