//位元組序列問題
目標機器:
小端序列(little-endian):低地址中存放的是字資料的低位元組,高地址存放的是字資料的高位元組
常用的x86,x64都是這種類型的
//同網路位元組序列
大端序列(big-endian):字資料的高位元組儲存在低地址中,而字資料的低位元組則存放在高地址中
Sun公司的機器Sparc架構
高<-----低
16bit寬的數0x1234,程式中此種情況為: 0x1234
地址:0x4000 0x4001
大端:0x12 0x34
小端:0x34 0x12
32bit寬的數0x12345678在Little-endian模式CPU記憶體中的存放方式(假設從地址0x4000開始存放)為:
記憶體位址 0x4000 0x4001 0x4002 0x4003
存放內容 0x78 0x56 0x34 0x12
而在Big-endian模式CPU記憶體中的存放方式則為:
記憶體位址 0x4000 0x4001 0x4002 0x4003
存放內容 0x12 0x34 0x56 0x78
雖然記憶體中儲存方法不同,但是程式中的寫法和表示方法一致,當涉及到二進位的處理的時候需要位元組序列轉換。
//檢查CPU類型
int checkCPU()
{
union w{
int a;
char b;
}c;
c.a = 1;
return(c.b ==1); //True 則從低到高儲存,小端
}
網路位元組序定義:
收到的第一個位元組被當作高位看待,這就要求發送端發送的第一個位元組應當是高位。
而在發送端發送資料時,發送的第一個位元組是該數字在記憶體中起始地址對應的位元組。
可見多位元組數值在發送前,在記憶體中數值應該以大端法存放。
網路位元組序說是大端位元組序。
//位元組序測試程式
//不同cpu平台上位元組序通常也不一樣,下面寫個簡單的C程式,它可以測試不同平台上的位元組序。
#include <stdio.h>
#include <netinet/in.h>
int main()
{
int i_num = 0x12345678;
printf("[0]:0x%x\n", *((char *)&i_num + 0));
printf("[1]:0x%x\n", *((char *)&i_num + 1));
printf("[2]:0x%x\n", *((char *)&i_num + 2));
printf("[3]:0x%x\n", *((char *)&i_num + 3));
i_num = htonl(i_num);
printf("[0]:0x%x\n", *((char *)&i_num + 0));
printf("[1]:0x%x\n", *((char *)&i_num + 1));
printf("[2]:0x%x\n", *((char *)&i_num + 2));
printf("[3]:0x%x\n", *((char *)&i_num + 3));
return 0;
}
//在80X86CPU平台上,執行該程式得到如下結果:
[0]:0x78
[1]:0x56
[2]:0x34
[3]:0x12
[0]:0x12
[1]:0x34
[2]:0x56
[3]:0x78
//通過對大小端的儲存原理分析可發現,對於 char 型資料,由於其只佔一個位元組,所以不存在這個問題,
//這也是一般情況下把資料緩衝區定義成 char 類型 的原因之一。對於 IP 位址、連接埠號碼等非 char 型資料,
//必須在資料發送到網路上之前將其轉換成大端模式,在接收到資料之後再將其轉換成符合接收端主機的儲存模式。
//Linux 系統為大小端模式的轉換提供了 4 個函數,輸入 man byteorder 命令可得函數原型:
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
//轉換函式演算法
DWORD ToLittleEndian(DWORD dwBigEndian)
{
BYTE* p = (BYTE*)&dwBigEndian;
return (DWORD)(p[0] << 24) + (DWORD)(p[1] << 16) +
(DWORD)(p[2] << 8) + (DWORD)p[3];
}