网络字节序和本地字节序的理解和实现

网络字节序和本地字节序的理解和实现

工作中经常用到这两个概念,看了APUE关于大端模式和小端模式的说明,和博文:htonl、ntohl、htons、ntohs函数实现 在这里概括归纳一下,权当备忘。以下所有假设都是在32位x86系统上。 
大端模式:网络字节序采用的模式,TCP/IP协议栈支持的模式 
小端模式:linux主机采用小端模式存储。

这里要注意不管是大端还是小端存储,MSB(数据最高字节)始终是在最左边,LSB(数据最低字节)始终是在最右边 
比如以十六进制的0x12345678为例,首先要了解十六进制一位表示4个二进制位,所以这个代表一个32位(8*4),4字节的数据,0x12是MSB,0x78是LSB 
对于大端模式MSB存储在低位字节地址n,LSB存储在高位字节地址n+3,这里的字节地址也是32位的,数据按照地址偏移按单字节存放。 
32整数内部的字节序

所以要将一个字符指针char* cp强制转换为这个整形地址,由于字节序的不同会带来差异。在小端机器上cp[0]指向最低位地址,存放的是0x78,然而cp[3]指向最高位地址,存放的是0x12;大端反之。 
可以用gdb调试查看,内存内部的变量分布,gdb内存查看命令: 
x /nfu addr 
说明 
x 是 examine 的缩写 
n表示要显示的内存单元的个数 
f表示显示方式, 可取如下值 
x 按十六进制格式显示变量。 
d 按十进制格式显示变量。 
u 按十进制格式显示无符号整型。 
o 按八进制格式显示变量。 
t 按二进制格式显示变量。 
a 按十六进制格式显示变量。 
i 指令地址格式 
c 按字符格式显示变量。 
f 按浮点数格式显示变量。 
u表示一个地址单元的长度 
b表示单字节, 
h表示双字节, 
w表示四字节, 
g表示八字节 
参考博文:gdb查看内存区命令

小端模式内存查看实验:

int main(int arc, char* arg[])
{
    unsingned int a = 0x12345678;
    while(1);
}

编译:
编译必须加-g选项才能生成可调试的文件
gcc littleEnding.c -g -o littleEnding

调试:
(gdb)gdb ./littleEnding
(gdb)b 4 //源文件第四行设置断点
(gdb)r //执行程序
(gdb)p &a //打印变量地址
$1 = (unsingned int *)0xbffff543
(gdb)x 0xbffff543 //查看内存单元内变量
0xbffff543: 0x12345678
(gdb) x /4xb 0xbffff543 //单字节查看4个内存单元变量的值
0xbffff543: 0x78 0x56 0x34 0x12
(gdb) x /1xb 0xbffff543
0xbffff543: 0x78
(gdb) x /2xb 0xbffff543
0xbffff543: 0x56
(gdb) x /3xb 0xbffff543
0xbffff543: 0x34
(gdb) x /4xb 0xbffff543
0xbffff543: 0x78 0x56 0x34 0x12
//可以看到内存最低地址单元存放的是数据的LSB 0x78
//显然linux x86下是小端的存放方式
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
typedef unsigned short int uint16;

typedef unsigned long int uint32;



// 短整型大小端互换

#define BigLittleSwap16(A)  ((((uint16)(A) & 0xff00) >> 8) | \

                            (((uint16)(A) & 0x00ff) << 8))

 // 长整型大小端互换



#define BigLittleSwap32(A)  ((((uint32)(A) & 0xff000000) >> 24) | \

                            (((uint32)(A) & 0x00ff0000) >> 8) | \

                            (((uint32)(A) & 0x0000ff00) << 8) | \

                            (((uint32)(A) & 0x000000ff) << 24))




 // 本机大端返回1,小端返回0

int checkCPUendian()

{

       union{

              unsigned long int i;

              unsigned char s[4];

       }c;



       c.i = 0x12345678;
//利用了联合的内存分配规则,共享内存仅分配一种数据结构
       return (0x12 == c.s[0]);

}



// 模拟htonl函数,本机字节序转网络字节序

unsigned long int t_htonl(unsigned long int h)

{

       // 若本机为大端,与网络字节序同,直接返回

       // 若本机为小端,转换成大端再返回

       return checkCPUendian() ? h : BigLittleSwap32(h);

}



// 模拟ntohl函数,网络字节序转本机字节序

unsigned long int t_ntohl(unsigned long int n)

{

       // 若本机为大端,与网络字节序同,直接返回

       // 若本机为小端,网络数据转换成小端再返回

       return checkCPUendian() ? n : BigLittleSwap32(n);

}



// 模拟htons函数,本机字节序转网络字节序

unsigned short int t_htons(unsigned short int h)

{

       // 若本机为大端,与网络字节序同,直接返回

       // 若本机为小端,转换成大端再返回

       return checkCPUendian() ? h : BigLittleSwap16(h);

}



// 模拟ntohs函数,网络字节序转本机字节序

unsigned short int t_ntohs(unsigned short int n)

{

       // 若本机为大端,与网络字节序同,直接返回

       // 若本机为小端,网络数据转换成小端再返回

       return checkCPUendian() ? n : BigLittleSwap16(n);

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值