网络通讯中字节排列顺序转化

来源:互联网 发布:汉诺塔程序JS 编辑:程序博客网 时间:2024/05/06 12:54

转载声明:本博客文章允许自由转载, 但转载需注明作者,出处
作者:denny ©wqf363@hotmail.com Nov 28, 2006
出处:www.huntmine.com

 一)字节排列顺序差异的来源

我们在写字符流时,因为字符型只占一个字节数,计算机只须按一个字符一个字符写入文件即可。但是如果是处理整型时,由于整型占4个字节,所以一个整型内部的字节存储排列的顺序直接关系到被计算机识别出来的整型值。某种意义上也可直接理解计算机的识别顺序就是所谓的字节顺序。
字节排列顺序是数据元素及其单个字节在内存中存储和表示时的顺序
 
不同的计算机结构有时使用不同的字节顺序存储数据。在将应用程序从一种架构类型迁移至另一种架构类型的过程中,经常会遇到字节排列顺序(endianness)问题。看附表:
MSB-
对取值影响最大的字节或位,以0x1234而言,12是MSB,通常指内容的左端.
LSB-对取值影响最小的字节或位,以0x1234而言,34是LSB,通常指内容的右端.
计算机结构
采用的字节顺序
别称
含义
Intel
Little-Endian
主机字节顺序
LSB先存储或传送.
Macintosh(Motorola)
Big-Endian
网络字节顺序
MSB先存储或传送
网络通讯
Big-Endian
 
 
 
另附:以下是一个处理器类型与对应的Endian的简表:
· Big Endian: Sun SPARC, Motorola 68000Java Virtual Machine
· Bi-Endian, 运行Big Endian模式: MIPS运行IRIX, PA-RISC,大多数PowerPowerPC系统
· Bi-Endian, 运行Little Endian模式: MIPS 运行Ultrix,大多数DEC Alpha, IA-64运行Linux
· Little Endian: Intel x86AMD64DEC VAX
     如何在程序中检测本系统的Endianess?可调用下面的函数来快速验证,如果返回值为1,则为Little Endian;为0则是Big Endian:
int testendian()
{

    int x = 1;
    return *((char *)&x);
}
PS: 趣谈Endian
(2008.11.26新增)
引用 http://www.eygle.com/digest/2007/01/whats_mean_endian.html
  1.  1、Definition
  2.   endian: The ordering of bytes in a multi-byte number.
  3.  2、Etymology 词源
  4. The term comes from Swift's "Gulliver's Travels" via the famous paper
  5. "On Holy Wars and a Plea for Peace" by Danny Cohen, USC/ISI IEN 137,
  6. 1980-04-01.
  7.   The Lilliputians, being very small, had correspondingly small political
  8. problems. The Big-Endian and Little-Endian parties debated over whether
  9. soft-boiled eggs should be opened at the big end or the little end.[From:
  10. Free On-Line Dictionary Of Computing or Jargon File](后缀ian表明的就是支持
  11. 某种观点的人:-)
  12.     1980年,Danny Cohen在其著名的论文"On Holy Wars and a Plea for Peace"
  13. 中为了平息一场关于在消息中字节该以什么样的顺序进行传送的争论而引用了该词。
  14. 该文中,Cohen非常形象贴切地把支持从一个消息序列的MSB开始传送的那伙人叫做
  15. Big-Endians,支持从LSB开始传送的相对应地叫做Little-Endians。此后Endian这
  16. 个词便随着这篇论文而被广为采用。
  17. 两个术语定义
  18.   1、MSB
  19.   MSB是Most Significant Bit/Byte的首字母缩写,通常译为最重要的位或者最
  20. 重要的字节。它通常用来表明在一个bit序列(如一个byte是8个bit组成的一个序
  21. 列)或者一个byte序列(如word是两个byte组成的一个序列)中对整个序列取值影
  22. 响最大的那个bit/byte
  23.   2、LSB
  24.   LSB是Least Significant Bit/Byte的首字母缩写,通常译为最不重要的位或
  25. 者最不重要的字节。它通常用来表明在一个bit序列(如一个byte是8个bit组成的
  26. 一个序列)或者一个byte序列(如word是两个byte组成的一个序列)中对整个序
  27. 列取值影响最小的那个bit/byte



)字节排列顺序出现的情形
出现字节排列顺序问题的原因之一是不一致的数据引用。它经常表现为由于数据元素转换、使用联合数据结构或使用和操作位域导致数据类型不匹配。
 1)不一致的数据引用
#include <stdio.h>
int main()
{
       int value;
       char *p;
       p = (char*) &value;
       value = 0x89ABCDEF;
       printf("%X.%X.%X.%X/n", p[0], p[1], p[2], p[3]);
       return 0;
}
输出:
FFFFFFEF.FFFFFFCD.FFFFFFAB.FFFFFF89
 
2)在不同架构下的结果
#include <unistd.h>
int main()
{
       int i=0x41424344;
       printf("int Address:%x Value:%x/n",&i,i);
       printf("-------------------------------/n");
 
       char* pAddress=(char*)&i;
       int j;
 
       for(j=0;j<=3;j++)
       {
              printf("char Address:%x Value:%c/n",pAddress,*pAddress);
              pAddress++;
       }
}
Little-Endian 模式输出:
int Address:bfe1b7f4 Value:41424344
-------------------------------
char Address:bfe1b7f4 Value:D
char Address:bfe1b7f5 Value:C
char Address:bfe1b7f6 Value:B
char Address:bfe1b7f7 Value:A
 
Big-Endian 模式输出:
int Address: 12ff7c Value:41424344
-------------------------------
char Address: 12ff7c Value:A
char Address: 12ff7d Value:B
char Address: 12ff7e Value:C
char Address: 12ff7f Value:D
 
)如何进行字节排列顺序转化
字节转换多半应用在网络编程,或者代码移植的情况下。
在网络通讯中,TCP/IP协议规定了专门的"网络字节次序",即无论计算机系统支持何种Endian,在传输数据时,总是数值最高位的字节最先发送。从定义可以看出,网络字节次序其实是对应Big Endian的。
 
为了避免因为Endianness造成的通信问题,及便于软件开发者编写易于平台移植的程序,特别定义了一些C语言预处理的宏来实现网络字节与主机字节次序之间的相互转换。htons()htonl()用来将主机字节次序转成网络字节次序,前者应用于16位无符号数,后者应用于32位无符号数。ntohs()ntohl()实现反方向的转换。这四个宏的原型定义可参考如下(Linux系统中可在netinet/in.h文件里找到)
htons()--"Host to Network Short"
        htonl()--"Host to Network Long"
        ntohs()--"Network to Host Short"
        ntohl()--"Network to Host Long"
bsd系统:
#if defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN)
#define htons(A) (A)
#define htonl(A) (A)
#define ntohs(A) (A)
#define ntohl(A) (A) 
#elif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)
#define htons(A) ((((uint16)(A) & 0xff00) >> 8) | /
                   (((uint16)(A) & 0x00ff) << 8))
#define htonl(A) ((((uint32)(A) & 0xff000000) >> 24) | /
                   (((uint32)(A) & 0x00ff0000) >> 8) | /
                   (((uint32)(A) & 0x0000ff00) << 8) | /
                   (((uint32)(A) & 0x000000ff) << 24))
#define ntohs     htons
#define ntohl     htohl
#else
#error "Either BIG_ENDIAN or LITTLE_ENDIAN must be #defined, but not both." 
#endif
特别经验:网络通讯中,为了在不同的平台下接收到的数据都是能正确解析的,要求发送以16bytes或32bytes为单位,在发送时将发送单位用htonl,htons都转化成网络字节顺序,在接收到数据后将接到的每个单位数据都用ntohl, ntohs转化成本机字节顺序, 这样就能保证在各个平台的数据都转化正确。