1
做了一個簡單的DHCP客戶端,深入了解了一下字節(jié)和類型。代碼片段如下:
static void DumpUInt(const char* title, unsigned int n) {
assert(title != NULL);
printf("%s %u\n", title n);
unsigned char* bytes = (unsigned char*)&n;
for (int i = 0; i < sizeof(n); ++i) {
printf("%02x ", bytes[i]);
}
printf("\n");
}
int main() {
// ...
/*
struct Buffer {
char* base;
size_t len;
}
*/
unsigned char*bytes = (unsigned char*)buf->base;
printf("network\n");
for (int i = 16; i < 20; ++i) {
printf("%02x ", bytes[i]);
}
printf("\n");
unsigned int* asInt = (unsigned int*)buf->base;
unsigned int ipbin = ntohl(asInt[4]);
unsigned char ip[4] = {0};
for (int i = 0; i < 4; ++i) {
ip[i] = (ipbin >> i * 8) & 0xFF;
}
LOGINFO("Offered IP: %d.%d.%d.%d", ip[3], ip[2], ip[1], ip[0]);
DumpUInt("asInt[4]", asInt[4]);
DumpUInt("ipbin", ipbin);
// ...
}
網(wǎng)絡(luò)數(shù)據(jù)存儲在buf->base
中弄兜。數(shù)據(jù)塊的第17-20字節(jié)存的是DHCP服務(wù)器返回的IP地址。一般我都會直接轉(zhuǎn)成字節(jié)流瓷式,去讀第多少個字節(jié)替饿。但參考的代碼使用了unsigned int
指針。這只是為了便利地訪問到想要的數(shù)據(jù):asInt[4]
蒿往。覺得這種方法挺有意思盛垦,就打印了一下值湿弦。打印結(jié)果如下:
network
0a 0b 3f 07
[18-07-26 XX:XX:XX][INFO]Offered IP: 10.11.63.7
asInt[4] 121572106
0a 0b 3f 07
ipbin 168509191
07 3f 0b 0a
這段代碼跑在Win10 X64上∪柯現(xiàn)在把感興趣的數(shù)據(jù)拿出來:
network
a. 0a 0b 3f 07 網(wǎng)絡(luò)序列
asInt[4]
121572106
b. 0a 0b 3f 07 網(wǎng)絡(luò)序列
c. 07 3f 0b 0a asInt[4]的十六進(jìn)制格式
ipbin
168509191
d. 07 3f 0b 0a 主機(jī)序列
e. 0a 0b 3f 07 ipbin的十六進(jìn)制格式
IP
10.11.63.7
f. 0a 0b 3f 07 最終的IP結(jié)果
從asInt[4]
和ipbin
的打印可以看到,無論內(nèi)存中是什么數(shù)據(jù),unsigned int
指針都會從起始位置讀取4個字節(jié)按照本機(jī)的序列來解析它蔬充。按類型讀取出的數(shù)據(jù)和ntohl
轉(zhuǎn)換出來的數(shù)據(jù)是一樣的蝶俱。這個結(jié)論在大端機(jī)上也適用。
看了一下glibc的源碼饥漫,發(fā)現(xiàn)ntohl
只是簡單的反序了參數(shù)的字節(jié)榨呆。如果知道讀取的內(nèi)容是什么類型時,可以不必給字節(jié)轉(zhuǎn)序庸队,直接使用該類型的指針讀取就好了积蜻。
2
字節(jié)序是已字節(jié)為最小單位的。如果收發(fā)的數(shù)據(jù)都是單字節(jié)數(shù)據(jù)彻消,那么不必考慮字節(jié)序的問題竿拆。不過,如果使用了bit filed特性宾尚,就需要考慮字節(jié)內(nèi)的位序了丙笋。大端單字節(jié)的位序和小端單字節(jié)的位序是相反的。按位讀取的時候如果不考慮位序就會出問題煌贴。例如御板,定義一個IP頭數(shù)據(jù)結(jié)構(gòu):
typedef struct IPHeader {
// {{
unsigned char ipHeaderLength : 4;
unsigned char ipVersion : 4;
// }}
unsigned char typeOfService;
unsigned short ipTotalLength;
unsigned short ipId;
// {{
unsigned char ipFragmentOffset : 5;
unsigned char ipMoreFragment : 1;
unsigned char ipDontFragment : 1;
unsigned char ipReservedZero : 1;
// }}
unsigned char ipFragmentOffset1;
unsigned char ipTTL;
unsigned char ipProtocol;
unsigned short ipChecksum;
unsigned int ipSrcAddr;
unsigned int ipDstAddr;
} IPV4Header, *PIPV4Header, FAR* LPIPV4Header;
上面IP頭的定義中Version和Header Length反了,同樣牛郑,保留0字段怠肋,不分片字段,分片字段淹朋,分片偏移的順序也反了灶似。這就是考慮了大端和小端的位序不同。