Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1530866
  • 博文数量: 226
  • 博客积分: 3997
  • 博客等级: 少校
  • 技术积分: 2369
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-19 17:26
个人简介

Never save something for a special occasion. Every day in your life is a special occasion.

文章分类

全部博文(226)

文章存档

2018年(5)

2017年(11)

2016年(1)

2015年(17)

2014年(14)

2013年(30)

2012年(5)

2011年(52)

2010年(107)

分类:

2010-07-25 15:30:38

字节序

目录

 

关键字:端模式、主机序、网络字节序

Endian 端模式

整数(short/int/long)各字节在内存中保存的顺序问题,称为Endian,又称 主机序(本地充).

Big-Endian(BE),就是高位字节(权值较大的字节)排放在内存的低地址端,低位字节排放在内存的高地址端。
Little-Endian(LE), 就是低位字节(权值较小的字节)排放在内存的低地址端,高位字节排放在内存的高地址端。

eg
双字 0x01020304(DWORD) 在内存中的分布
地址 4000 4001 4002 4003
LE 04 03 02 01
BE 01 02 03 04

特点:
Big-Endian,类似字符串的存储,直观。
Little-Endian,符合人的一般思维(第一感觉), 低位值小,存放在内存地址小的地方; ...

不同平台的端模式:

小端模式——X86系列,很多的ARM、DSP都为小端模式,...
小端模式——PowerPC,KEIL C51
可选端模式——有些ARM处理器还可以由硬件(跳线)来选择是大端模式还是小端模式。

端模式测试:

short int x;   
char x0,x1;   
x=0x1122;   
x0=((char*)&x)[0]; //低地址单元   
x1=((char*)&x)[1]; //高地址单元  
 
若x0=0x11,则是大端; 若x0=0x22,则是小端
这里也可以看出,数据寻址时,用的是低位字节的地址。

或者

bool IsBig_Endian()
{
unsigned short test = 0x1234;
if(*( (unsigned char*) &test ) == 0x12)
return TRUE;
else
return FALSE;
}//IsBig_Endian()

转换

对于WORD(2字节),高低字节颠倒.
对于DWORD(4字节),先两个WORD间颠倒, 然后两个WORD再各自颠倒。

对于WORD型
#define cvt_end2(s) (((s<<8) & 0xFF00) | ((s>>8) & 0xFF))

对于DWORD型
#define cvt_end4(s) (((s<<24) & 0xFF000000) | ((s<<8) & 0xFF0000) | \
((s>>8) & 0xFF00) | ((s>>24) & 0xFF))

网络字节序

网络字节序是 TCP/IP 中规定的一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,从而保证数据在不同主机之间传输时能够被正确解释。
网络字序采用 big endian 方式。

不同平台之间的数据交换

1、内存块传输
先按字节传输,传输后再进行字节序的转换。
2、将整数转换为网络字节序后再传输
先转换为网络字节序,再传输。

示例:
PCbig 大端的平台
PClittle 小端平台
两个平台上的程序都定义如下数据结构

typedef struct pack_tag
{
unsigned short a;
unsigned short b;
}PACK;
最先声明的成员,分配在最低地址。
关于结构内存分布的细节,结构体一节再讲。

现在要将2个unsigned short数据 0x1234, 0x5678 从 PCbig 传输到 PClittle 上,可以通过上面提到的2种方式进行。

方式一:内存块传输

PCbig
BYTE* pBuffer = new BYTE[sizeof(PACK)]; // 申请内存
PACK *pPackSend = (PACK*)pBuffer; // 指向内存
pPackSBuf->a = 0x1234; // 将数据存储在结构中
pPackSBuf->b = 0x5678;
SockSendBuf(pBuffer, sizeof(PACK)); // 数据块发送到PClittle

PClittle
BYTE* pBuffer = new BYTE[sizeof(PACK)]; // 申请内存
SockRecvBuf(pBuffer, sizeof(PACK)); // 从PCbig接收数据块
PACK pack = *((PACK*)pBuffer); // 取得数据块
pack.a = cvt_end(pack.a); // 端模式转换
pack.b = cvt_end(pack.b);

废话
数据包在PCbit的内存分布如下
... [12][34] [56][78] ...
数据块发送到PClittle后,在内存分布仍然是这样,
如果不转换而直接通过结构体体去获得数据,则
pack.a = 0x3412
pack.b = 0x7856
通过上面的端模式转换后,数据的内存分布如下
[12][34] [56][78]
此时通过结构体去解析才能获得正确的数据
pack.a = 0x1234
pack.b = 0x5678

方式二:按网络字节序传输

PCbig
BYTE* pBuffer = new BYTE[sizeof(PACK)]; // 申请内存
PACK *pPackSend = (PACK*)pBuffer; // 指向内存
pPackSBuf->a = htons(0x1234); // 将数据按网络字节序存储在结构中
pPackSBuf->b = htons(0x5678);
SockSendBuf(pBuffer, sizeof(PACK)); // 数据块发送到PClittle

PClittle
BYTE* pBuffer = new BYTE[sizeof(PACK)]; // 申请内存
SockRecvBuf(pBuffer, sizeof(PACK)); // 从PCbig接收数据块
PACK pack = *((PACK*)pBuffer); // 取得数据块
pack.a = ntohs(pack.a); // 转换为本机序
pack.b = ntohs(pack.b);

关于网络字节序的更多内容,见 网络编程->网络字节序 一节。

阅读(1210) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~