网络字节顺序大端法小端法.docx
《网络字节顺序大端法小端法.docx》由会员分享,可在线阅读,更多相关《网络字节顺序大端法小端法.docx(10页珍藏版)》请在冰豆网上搜索。
网络字节顺序大端法小端法
网络字节顺序、大端法、小端法
分类:
j2se2006-03-2323:
533213人阅读评论
(1)收藏举报
在国内的4种短信协议的协议头部分,都定义了4个字节长度的messagelength字段,字段的数据类型为无符号整形(也就是说,这个字段的范围是0-2^16-1);而在java语言中,没有无符号整形这种数据类型(如果用int类型来表示,由于java中int型是有符号数,则会发送溢出),我设想将messagelength存入long类型中,将数字的大小控制在0-2^16-1范围之内,当超过此范围归零重新开始。
在网络传输时,将long类型先转化为byte数组,步骤如下:
longl;
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的publicvoidwrite(byte[] b,int off,int len)方法来向Socket写字节流
,写byte[0]至byte[3]的字节。
java.io
ClassOutputStream
write
publicabstractvoidwrite(int b)throwsIOException
Writesthespecifiedbytetothisoutputstream.Thegeneralcontractforwriteisthatonebyteiswrittentotheoutputstream.Thebytetobewrittenistheeightlow-orderbitsoftheargumentb.The24high-orderbitsofbareignored.
SubclassesofOutputStreammustprovideanimplementationforthismethod.
Parameters:
b-thebyte.
Throws:
IOException-ifanI/Oerroroccurs.Inparticular,anIOExceptionmaybethrowniftheoutputstreamhasbeenclosed.
write
publicvoidwrite(byte[] b,int off,int len)throwsIOException
Writeslenbytesfromthespecifiedbytearraystartingatoffsetofftothisoutputstream.Thegeneralcontractforwrite(b,off,len)isthatsomeofthebytesinthearraybarewrittentotheoutputstreaminorder;elementb[off]isthefirstbytewrittenandb[off+len-1]isthelastbytewrittenbythisoperation.
ThewritemethodofOutputStreamcallsthewritemethodofoneargumentoneachofthebytestobewrittenout.Subclassesareencouragedtooverridethismethodandprovideamoreefficientimplementation.
Ifbisnull,aNullPointerExceptionisthrown.
Ifoffisnegative,orlenisnegative,oroff+lenisgreaterthanthelengthofthearrayb,thenanIndexOutOfBoundsExceptionisthrown.
Parameters:
b-thedata.
off-thestartoffsetinthedata.
len-thenumberofbytestowrite.
Throws:
IOException-ifanI/Oerroroccurs.Inparticular,anIOExceptionisthrowniftheoutputstreamisclosed.
------关于网络、主机字节顺序的文章
主机和网络字节序的转换
最近使用C#进行网络开发,需要处理ISO8583报文,由于其中有些域是数值型的,于是在传输的时候涉及到了字节序的转换。
字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有两种字节顺序,根据他们所处的位置我们分别称为主机节序和网络字节序。
通常我们认为网络字节序为标准顺序,封包的时候,将主机字节序转换为网络字节序,拆包的时候要将网络字节序转换为主机字节序。
原以为还要自己写函数,其实网络库已经提供了。
主机到网络:
short/int/longIPAddress.HostToNetworkOrder(short/int/long)
网络到主机:
short/int/longIPAddress.NetworkToHostOrder(short/int/long)
主机字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处,如:
intx=1;//此时x为主机字节序:
[1][0][0][0]低位到高位
inty=65536//此时y为主机字节序:
[0][0][1][0]低位到高位
我们通过主机到网络字节序的转换函数分别对x和y进行转换得到他们对应的网络字节序值,网络节序是高字节数据存放在低地址处,低字节数据存放在高地址处,如:
intm=IPAddress.HostToNetworkOrder(x);
//此时m为主机字节序:
[0][0][0][1]高位到低位
intn=IPAddress.HostToNetworkOrder(y);
//此时n为主机字节序:
[0][1][0][0]高位到低位
经过转换以后,我们就可以通过
byte[]btValue=BitConverter.GetBytes(m);
得到一个长度为4的byte数组,然后将这个数组设置到报文的相应位置发送出去即可。
同样,收到报文后,可以将报文按域拆分,得到btValue,使用
intm=BitConverter.ToInt32(btValue,0);//从btValue的第0位开始转换
得到该域的值,此时还不能直接使用,应该再用网络到主机字节序的转换函数进行转换:
intx=IPAddress.NetworkToHostOrder(m);
这时得到的x才是报文中的实际值。
---------------------------------------------------
也谈字节序问题
一次SunSPARC到IntelX86的平台移植让我们的程序遭遇了“字节序问题”,既然遇到了也就不妨深入的学习一下。
一、字节序定义
字节序,顾名思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)。
其实大部分人在实际的开发中都很少会直接和字节序打交道。
唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。
在所有的介绍字节序的文章中都会提到字节序分为两类:
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
以上图为例如果我们在栈上分配一个unsignedcharbuf[4],那么这个数组变量在栈上是如何布局的呢[注1]?
看下图:
栈底(高地址)
----------
buf[3]
buf[2]
buf[1]
buf[0]
----------
栈顶(低地址)
现在我们弄清了高低地址,接着我来弄清高/低字节,如果我们有一个32位无符号整型0x12345678(呵呵,恰好是把上面的那4个字节buf看成一个整型),那么高位是什么,低位又是什么呢?
其实很简单。
在十进制中我们都说靠左边的是高位,靠右边的是低位,在其他进制也是如此。
就拿0x12345678来说,从高位到低位的字节依次是0x12、0x34、0x56和0x78。
高低地址和高低字节都弄清了。
我们再来回顾一下Big-Endian和Little-Endian的定义,并用图示说明两种字节序:
以unsignedintvalue=0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsignedcharbuf[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。
三、例子
测试平台:
SunSPARCSolaris9和IntelX86Solaris9
我们的例子是这样的:
在使用不同字节序的平台上使用相同的程序读取同一个二进制文件的内容。
生成二进制文件的程序如下:
/*gen_binary.c*/
intmain(){
FILE *fp=NULL;
int value=0x12345678;
int rv=0;
fp=fopen("temp.dat","wb");
if(fp==NULL){
printf("fopenerror/n");
return-1;
}
rv=fwrite(&value,sizeof(value),1,fp);
if(rv!
=1){
printf("fwriteerror/n");
return-1;
}
fclose(fp);
return0;
}
读取二进制文件的程序如下:
intmain(){
int value =0;
FILE *fp =NULL;
int rv =0;
unsigned charbuf[4];
fp=fopen("temp.dat","rb");
if(fp==NULL){
printf("fopenerror/n");
return-1;
}
rv=fread(buf,sizeof(unsignedchar),4,fp);
if(rv!
=4){
printf("freaderror/n");
return-1;
}
memcpy(&value,buf,4);//orvalue=*((int*)buf);
printf("thevalueis%x/n",value);
fclose(fp);
return0;
}
测试过程:
(1)在SPARC平台下生成temp.dat文件
在SPARC平台下读取temp.dat文件的结果:
thevalueis12345678
在X86平台下读取temp.dat文件的结果:
thevalueis78563412
(1)在X86平台下生成temp.dat文件
在SPARC平台下读取temp.dat文件的结果:
thevalueis78563412
在X86平台下读取temp.dat文件的结果:
thevalueis12345678
[注1]
buf[4]在栈的布局我也是通过例子程序得到的:
intmain(){
unsignedcharbuf[4];
printf("thebuf[0]addris%x/n",buf);
printf("thebuf[1]addris%x/n",&buf[1]);
return0;
}
output:
SPARC平台:
thebuf[0]addrisffbff788
thebuf[1]addrisffbff789
X86平台:
thebuf[0]addris8047ae4
thebuf[1]addris8047ae5
两个平台都是buf[x]所在地址高于buf[y](x>y)。