golang之大端序、小端序
一、概述
位元組序:位元組在電腦中存放時的序列與輸入/輸出時的序列;也指的是存放多位元組資料的位元組(byte)的順序,典型的情況是整數在記憶體中的存放方式和網路傳輸的傳輸順序。
先看下基本概念:
- 1、大端模式(Big endian):將高序位元組儲存在起始地址(按照從低地址到高地址的順序存放資料的高位位元組到低位位元組)
- 2、小端模式(Little endian):將低序位元組儲存在起始地址(按照從低地址到高地址的順序存放據的低位位元組到高位位元組)
在計算機領域中,大小端序是跟硬體的體系結構有關的。
舉個例子:如一個 var a = 0x11223344,對於這個變數的最高位元組為0x11,最低位元組為0x44。假設在記憶體中分配地址如下(地址都是連續的)
當分別處於大小端模式下的內容存放如下
(1)大端模式儲存(儲存地址為16位)
地址 資料
0x0004(高地址) 0x44
0x0003 0x33
0x0002 0x22
0x0001(低地址) 0x11
(2)小端模式儲存(儲存地址為16位)
地址 資料
0x0004(高地址) 0x11
0x0003 0x22
0x0002 0x33
0x0001(低地址) 0x44
二、大端序和小端序
在前面也簡單闡述了大小端序的定義並結合簡單例項來說明,接下來會給出詳細例項來說明:
1、大端序(Big-Endian):或稱大尾序
一個型別: int32 的數 0X0A0B0C0D的記憶體存放情況
資料是以8bits為單位
示例中,最高有效位是將0x0A儲存在最低的記憶體地址處,接著是0x0B存在後面的地址,類似十六進位制位元組從左往右的順序。
資料以16bits為單位
最高的16bit單元0x0A0B儲存在低位
2、小端序(little-endian):或稱小尾序
資料以8bits為單位
示例中最低有效位則是0x0D儲存的記憶體地址處,後面依次存放在後面的地址處。
資料以16bits為單位
最低的16bit單元0x0C0D儲存在低位。
3、總結
採用大端序的CPU和採用小端序的CPU不僅在位元組上是相反的,在位元位上也是相反的。
比如0x01在記憶體中的儲存
大端序:記憶體低位元位 00000001 記憶體高位元位
小端序:記憶體低位元位 10000000 記憶體高位元位
比如0x00000001
大端序:記憶體低位元位 00000000 00000000 00000000 00000001 記憶體高位元位
小端序:記憶體低位元位 10000000 00000000 00000000 00000000 記憶體高位元位
三、應用
其實在前面羅列出那麼東西,最終是為了接下來講述的在golang中涉及到網路傳輸、檔案儲存時的選擇。一般來說網路傳輸的位元組序,可能是大端序或者小端序,取決於軟體開始時通訊雙方的協議規定。TCP/IP協議RFC1700規定使用“大端”位元組序為網路位元組序,開發的時候需要遵守這一規則。預設golang是使用大端序。詳情見golang中包encoding/binary已提供了大、小端序的使用
import (
"encoding/binary"
"fmt"
)
func BigEndian() { // 大端序
// 二進位制形式:0000 0000 0000 0000 0001 0002 0003 0004
var testInt int32 = 0x01020304 // 十六進位制表示
fmt.Printf("%d use big endian: \n", testInt)
var testBytes []byte = make([]byte, 4)
binary.BigEndian.PutUint32(testBytes, uint32(testInt)) //大端序模式
fmt.Println("int32 to bytes:", testBytes)
convInt := binary.BigEndian.Uint32(testBytes) //大端序模式的位元組轉為int32
fmt.Printf("bytes to int32: %d\n\n", convInt)
}
func LittleEndian() { // 小端序
//二進位制形式: 0000 0000 0000 0000 0001 0002 0003 0004
var testInt int32 = 0x01020304 // 16進位制
fmt.Printf("%d use little endian: \n", testInt)
var testBytes []byte = make([]byte, 4)
binary.LittleEndian.PutUint32(testBytes, uint32(testInt)) //小端序模式
fmt.Println("int32 to bytes:", testBytes)
convInt := binary.LittleEndian.Uint32(testBytes) //小端序模式的位元組轉換
fmt.Printf("bytes to int32: %d\n\n", convInt)
}
func main() {
BigEndian()
LittleEndian()
}
輸出結果:
16909060 use big endian:
int32 to bytes: [1 2 3 4] ### [0001 0002 0003 0004]
bytes to int32: 16909060
16909060 use little endian:
int32 to bytes: [4 3 2 1] ### [0004 0003 0002 0001]
bytes to int32: 16909060
四、RPCX
在RPCX框架中關於RPC呼叫過程涉及的傳遞訊息進行編碼的,採用的就是大端序模式
func (m Message) Encode() []byte { // 編碼訊息
// 編碼metadata將key-value轉為key=value&key=value形式
meta := encodeMetadata(m.Metadata)
spL := len(m.ServicePath) // 服務長度
smL := len(m.ServiceMethod) // 服務函式
var err error
payload := m.Payload // 訊息體
if m.CompressType() != None { // 壓縮
compressor := Compressors[m.CompressType()]
if compressor == nil { // 預設使用None壓縮型別
m.SetCompressType(None)
} else {
payload, err = compressor.Zip(m.Payload) // GZIP壓縮
if err != nil { // 壓縮失敗 不對傳輸訊息進行壓縮
m.SetCompressType(None)
payload = m.Payload
}
}
}
// RPCX資料包 = header + ID + total size +
// 服務名及內容: servicePath(size(servicePath) 、len(servicePath)) +
// 服務函式及內容:serviceMethod(size(serviceMethod) 、 len(serviceMethod)) +
// 後設資料及內容: metadata(size(metadata) 、len(metadata)) +
// 訊息體及內容:payload(size(payload) 、 len(payload))
// 訊息長度 = size(servicePath) + len(servicePath) + size(serviceMethod)
// + len(serviceMethod) + size(metadata) + len(metadata)
// + size(payload) + len(payload)
totalL := (4 + spL) + (4 + smL) + (4 + len(meta)) + (4 + len(payload))
// header + dataLen + spLen + sp + smLen + sm
// + metaL + meta + payloadLen + payload
metaStart := 12 + 4 + (4 + spL) + (4 + smL) // meata開始位置
payLoadStart := metaStart + (4 + len(meta)) // payLoad開始位置
l := 12 + 4 + totalL
data := make([]byte, l)
copy(data, m.Header[:]) // 拷貝header內容
// 將資料包以大端序模式進行編碼
//totalLen
binary.BigEndian.PutUint32(data[12:16], uint32(totalL)) //
binary.BigEndian.PutUint32(data[16:20], uint32(spL))
copy(data[20:20+spL], util.StringToSliceByte(m.ServicePath))
binary.BigEndian.PutUint32(data[20+spL:24+spL], uint32(smL))
copy(data[24+spL:metaStart], util.StringToSliceByte(m.ServiceMethod))
binary.BigEndian.PutUint32(data[metaStart:metaStart+4], uint32(len(meta)))
copy(data[metaStart+4:], meta)
binary.BigEndian.PutUint32(data[payLoadStart:payLoadStart+4],
uint32(len(payload)))
copy(data[payLoadStart+4:], payload)
return data
}
相關文章
- 大端序、小端序、網路位元組序
- C# 中大端序與小端序C#
- 分享:大端小端-位元組儲存順序
- 網路通訊時位元組序轉換原理與網路位元組序、大端和小端模式模式
- 深入淺出大端和小端
- 尤拉序的小技巧
- 【字典序第k小】
- 《程式碼之美》 ——序
- 計算機系統中的大端模式和小端模式計算機模式
- 大端(Big Endian)與小端(Little Endian)簡介
- JavaScript專題之亂序JavaScript
- 中序先序到後序 洛谷1827
- gitignore優先順序小結Git
- 應用序 or 正則序?
- 還原二叉樹(先序+中序-〉後序)二叉樹
- 二叉樹的四種遍歷方法:先序,中序,後序,層序二叉樹
- Python順序集合之 tuplePython
- Python順序集合之 ListPython
- 譯者序、序、撰稿人
- Qt 大小端位元組序的處理QT
- 【引用】『轉』【大端(Big Endian)與小端(Little Endian)簡介】
- css選擇器順序的小技巧CSS
- 二叉樹先知道後序和中序,求先序二叉樹
- 遞迴和迭代實現二叉樹先序、中序、後序和層序遍歷遞迴二叉樹
- dfs序
- UML建模之時序圖(Sequence Diagram)時序圖
- 【二叉樹】前中序求後序,中後序求前序二叉樹
- 非遞迴遍歷二叉樹的四種策略-先序、中序、後序和層序遞迴二叉樹
- 【Algorithm】二分排序小技巧Go排序
- load 與initialize的呼叫順序小結
- 二叉樹 前序、中序、後序二叉樹
- C/C++ 判斷計算機儲存器位元組序(端序)的幾種方式C++計算機
- 資料在記憶體中儲存的方式:大端模式與小端模式記憶體模式
- 後序+中序(前序+中序)重構樹,嚴格O(N)演算法演算法
- 先序、中序、後序序列的二叉樹構造演算法二叉樹演算法
- 已知二叉樹的先序和後序求任意一中序二叉樹
- vue 序號,翻頁時序號遞增Vue
- Git調整commit之間順序GitMIT