网络字节顺序、大端法、小端法

转载 2006年06月25日 11:44:00

在国内的4种短信协议的协议头部分,都定义了4个字节长度的message length字段,字段的数据类型为无符号整形(也就是说,这个字段的范围是0-2^16-1);而在java语言中,没有无符号整形这种数据类型(如果 用int类型来表示,由于java中int型是有符号数,则会发送溢出),我设想将message length存入long类型中,将数字的大小控制在0-2^16-1范围之内,当超过此范围归零重新开始。

在网络传输时,将long类型先转化为byte数组,步骤如下:

long l;

byte[] b;

b[0]=(byte)(l>>>24);

b[1]]=(byte)(l>>>16);

b[2]]=(byte)(l>>>8);

b[3]]=(byte)(l);

此时,b[]中就按照网络字节顺序(大端法,即l的高位数据存放在byte[]的低位地址,因为地址是
从低向高发展的)存放着4个bytes的数据
使用OutputStream的public void write(byte[] b,int off,int len)方法来向Socket写字节流
,写byte[0]至byte[3]的字节。

 

java.io
Class OutputStream

write

public abstract void write(int b)
throws IOException
Writes the specified byte to this output stream. The general contract for write is that one byte is written to the output stream. The byte to be written is the eight low-order bits of the argument b. The 24 high-order bits of b are ignored.

Subclasses of OutputStream must provide an implementation for this method.

 

Parameters:
b - the byte.
Throws:
IOException - if an I/O error occurs. In particular, an IOException may be thrown if the output stream has been closed.

write

public void write(byte[] b,
int off,
int len)
throws IOException
Writes len bytes from the specified byte array starting at offset off to this output stream. The general contract for write(b, off, len) is that some of the bytes in the array b are written to the output stream in order; element b[off] is the first byte written and b[off+len-1] is the last byte written by this operation.

The write method of OutputStream calls the write method of one argument on each of the bytes to be written out. Subclasses are encouraged to override this method and provide a more efficient implementation.

If b is null, a NullPointerException is thrown.

If off is negative, or len is negative, or off+len is greater than the length of the array b, then an IndexOutOfBoundsException is thrown.

 

Parameters:
b - the data.
off - the start offset in the data.
len - the number of bytes to write.
Throws:
IOException - if an I/O error occurs. In particular, an IOException is thrown if the output stream is closed.

------关于网络、主机字节顺序的文章

http://www-128.ibm.com/developerworks/cn/java/l-datanet/index.html

 

主机和网络字节序的转换

最近使用C#进行网络开发,需要处理ISO8583报文,由于其中有些域是数值型的,于是在传输的时候涉及到了字节序的转换。字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有两种字节顺序,根据他们所处的位置我们分别称为主机节序和网络字节序。

通常我们认为网络字节序为标准顺序,封包的时候,将主机字节序转换为网络字节序,拆包的时候要将网络字节序转换为主机字节序。原以为还要自己写函数,其实网络库已经提供了。

主机到网络:short/int/long IPAddress.HostToNetworkOrder(short/int/long)

网络到主机:short/int/long IPAddress.NetworkToHostOrder(short/int/long)

 

主机字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处,如:

int x=1;    //此时x为主机字节序:[1][0][0][0] 低位到高位

int y=65536 //此时y为主机字节序:[0][0][1][0] 低位到高位

我们通过主机到网络字节序的转换函数分别对xy进行转换得到他们对应的网络字节序值,网络节序是高字节数据存放在低地址处,低字节数据存放在高地址处,如:

int m=IPAddress.HostToNetworkOrder(x);

//此时m为主机字节序:[0][0][0][1] 高位到低位

int n=IPAddress.HostToNetworkOrder(y);

//此时n为主机字节序:[0][1][0][0] 高位到低位

 

经过转换以后,我们就可以通过

byte[]btValue=BitConverter.GetBytes(m);

得到一个长度为4byte数组,然后将这个数组设置到报文的相应位置发送出去即可。

同样,收到报文后,可以将报文按域拆分,得到btValue,使用

int m=BitConverter.ToInt32(btValue,0);//btValue的第0位开始转换

得到该域的值,此时还不能直接使用,应该再用网络到主机字节序的转换函数进行转换:

int x=IPAddress.NetworkToHostOrder(m);

这时得到的x才是报文中的实际值。

---------------------------------------------------

也谈字节序问题

http://bigwhite.blogbus.com/logs/2005/09/

一次Sun SPARC到Intel X86的平台移植让我们的程序遭遇了“字节序问题”,既然遇到了也就不妨深入的学习一下。

一、字节序定义
字节序,顾名思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)。

其实大部分人在实际的开发中都很少会直接和字节序打交道。唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。

在所有的介绍字节序的文章中都会提到字节序分为两类:Big-Endian和Little-Endian。引用标准的Big-Endian和Little-Endian的定义如下:
a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
c) 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。

其实我在第一次看到这个定义时就很糊涂,看了几个例子后也很是朦胧。什么高/低地址端?又什么高低位?翻阅了一些资料后略有心得。

二、高/低地址与高低字节
首先我们要知道我们C程序映像中内存的空间布局情况:在《C专家编程》中或者《Unix环境高级编程》中有关于内存空间布局情况的说明,大致如下图:
----------------------- 最高内存地址 0xffffffff
 | 栈底
 .
 .              栈
 .
  栈顶
-----------------------
 |
 |
/|/

NULL (空洞) 

/|/
 |
 |
-----------------------
                堆
-----------------------
未初始化的数据
----------------(统称数据段)
初始化的数据
-----------------------
正文段(代码段)
----------------------- 最低内存地址 0x00000000

以上图为例如果我们在栈上分配一个unsigned char buf[4],那么这个数组变量在栈上是如何布局的呢[注1]?看下图:
栈底 (高地址)
----------
buf[3]
buf[2]
buf[1]
buf[0]
----------
栈顶 (低地址)

现在我们弄清了高低地址,接着我来弄清高/低字节,如果我们有一个32位无符号整型0x12345678(呵呵,恰好是把上面的那4个字节buf看 成一个整型),那么高位是什么,低位又是什么呢?其实很简单。在十进制中我们都说靠左边的是高位,靠右边的是低位,在其他进制也是如此。就拿 0x12345678来说,从高位到低位的字节依次是0x12、0x34、0x56和0x78。

高低地址和高低字节都弄清了。我们再来回顾一下Big-Endian和Little-Endian的定义,并用图示说明两种字节序:
以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value:
Big-Endian: 低地址存放高位,如下图:
栈底 (高地址)
---------------
buf[3] (0x78) -- 低位
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) -- 高位
---------------
栈顶 (低地址)

Little-Endian: 低地址存放低位,如下图:
栈底 (高地址)
---------------
buf[3] (0x12) -- 高位
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) -- 低位
---------------
栈顶 (低地址)

在现有的平台上Intel的X86采用的是Little-Endian,而像Sun的SPARC采用的就是Big-Endian。

三、例子
测试平台: Sun SPARC Solaris 9和Intel X86 Solaris 9
我们的例子是这样的:在使用不同字节序的平台上使用相同的程序读取同一个二进制文件的内容。
生成二进制文件的程序如下:
/* gen_binary.c */
int main() {
        FILE    *fp = NULL;
        int     value = 0x12345678;
        int     rv = 0;

        fp = fopen("temp.dat", "wb");
        if (fp == NULL) {
                printf("fopen error/n");
                return -1;
        }

        rv = fwrite(&value, sizeof(value), 1, fp);
        if (rv != 1) {
                printf("fwrite error/n");
                return -1;
        }

        fclose(fp);
        return 0;
}

读取二进制文件的程序如下:
int main() {
        int             value   = 0;
        FILE         *fp     = NULL;
        int             rv      = 0;
        unsigned        char buf[4];

        fp = fopen("temp.dat", "rb");
        if (fp == NULL) {
                printf("fopen error/n");
                return -1;
        }

        rv = fread(buf, sizeof(unsigned char), 4, fp);
        if (rv != 4) {
                printf("fread error/n");
                return -1;
        }

        memcpy(&value, buf, 4); // or value = *((int*)buf);
        printf("the value is %x/n", value);

        fclose(fp);
        return 0;
}

测试过程:
(1) 在SPARC平台下生成temp.dat文件
在SPARC平台下读取temp.dat文件的结果:
the value is 12345678

在X86平台下读取temp.dat文件的结果:
the value is 78563412

(1) 在X86平台下生成temp.dat文件
在SPARC平台下读取temp.dat文件的结果:
the value is 78563412

在X86平台下读取temp.dat文件的结果:
the value is 12345678

[注1]
buf[4]在栈的布局我也是通过例子程序得到的:
int main() {
        unsigned char buf[4];

        printf("the buf[0] addr is %x/n", buf);
        printf("the buf[1] addr is %x/n", &buf[1]);

        return 0;
}
output:
SPARC平台:
the buf[0] addr is ffbff788
the buf[1] addr is ffbff789
X86平台:
the buf[0] addr is 8047ae4
the buf[1] addr is 8047ae5

两个平台都是buf[x]所在地址高于buf[y] (x > y)。

大端字节序和小端字节序

对表示一个对象的字节序列排序,有两个同用的规则。有些机器选择在存储器中按照从最低有效字节到最高有效字节的顺序存储对象,而另一些机器按照从最高有效字节到最低有效字节的顺序存储。前一种规则——最低有效字节...
  • qq_33724710
  • qq_33724710
  • 2016年04月04日 14:50
  • 1696

大端法还是小端法区别

第一部分 基本概念 首先不管是大端法还是小端法存储,计算机在内存中存放数据的顺序都是从低地址到高地址,所不同的是首先取低字节的数据存放在低地址还是取高字节数据存放在低地址。 若首先取高字节的数据存...
  • chudongfang2015
  • chudongfang2015
  • 2016年05月03日 16:03
  • 427

处理器字节大小端存储方式、网络字节顺序(总结)

1、为什么会有大小端模式之分呢?         这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8bit。但是在C语言中除了8bit的char之外,还有16b...
  • youk110
  • youk110
  • 2017年01月04日 13:05
  • 489

大端模式和小端模式转化

在工作中遇到一个问题,数据是以大端模式存储的,而机器是小端模式,必须进行转换,否则使用时会出问题。 一、定义: 大端模式(Big Endian):数据的高字节,保存在内存的低地址中;数据的低字节,保存...
  • szchtx
  • szchtx
  • 2015年01月18日 12:20
  • 15098

大端法与小端法

第一部分 基本概念 首先不管是大端法还是小端法存储,计算机在内存中存放数据的顺序都是从低地址到高地址,所不同的是首先取低字节的数据存放在低地址还是取高字节数据存放在低地址。 若首先取高字节的数据存...
  • u012861978
  • u012861978
  • 2016年07月14日 10:05
  • 263

字节存储排序:大端和小端的判别及转换

当前的存储器,多以byte为访问的最小单元,当一个逻辑上的地址必须分割为物理上的若干单元时就存在了先放谁后放谁的问题,于是端(endian)的问题应运而生了,对于不同的存储方法,就有大端(big-en...
  • u010193457
  • u010193457
  • 2016年09月14日 22:43
  • 2866

[c/c++ ]字节序与大小端转换

注明: 以下内容均为学习内容整理,记录,便于自己学习,并非完全意义上的自产,如有感到不适,请联系我 一.多字节值及字节序 1.brief 现在有一个数字 65430,这个...
  • kuai0705
  • kuai0705
  • 2014年03月09日 13:55
  • 18519

linux kernel如何处理大端小端字节序

根据之前的理解,字节序可以认为是处理器主观的概念,就像人如何去看待事物一样,处理器分大端和小端,对于内存的读写,只要保证数据类型一致,就不存在字节序的问题。 因此我感觉,字节序不同造成的最大差异在于对...
  • skyflying2012
  • skyflying2012
  • 2015年02月13日 10:53
  • 4453

跨平台网络数据传输过程中的主机字节顺序和网络字节顺序

大端和小端: 大端:在低地址存放高字节,在高地址存放低字节 小端:在低地址存放低字节,在高地址存放高字节   注意:地址是左边是低地址,右边是高地址,字节是左边是高字节,右边是低字节。采用大端法存储变...
  • qq51931373
  • qq51931373
  • 2015年05月30日 21:33
  • 1573

《unix网络编程》(6)网络字节序 大端模式和小端模式

大端模式和小端模式的定义 a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内...
  • u013074465
  • u013074465
  • 2015年03月24日 10:03
  • 1459
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:网络字节顺序、大端法、小端法
举报原因:
原因补充:

(最多只允许输入30个字)