文档章节

网络字节顺序

蜀山下的鱼
 蜀山下的鱼
发布于 2015/04/29 00:39
字数 2893
阅读 9
收藏 0
字节序,顾名思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)。其实大部分人在实际的开发中都很少会直接和字节序打交道。唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。
在所有的介绍字节序的文章中都会提到字节序分为两类:Big-Endian和Little-Endian,引用标准的Big-Endian和Little-Endian的定义如下:
a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
c) 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。


JAVA字节序
BIG-ENDIAN、LITTLE-ENDIAN跟多字节类型的数据有关,比如int,short,long型,而对单字节数据byte却没有影响。BIG-ENDIAN就是低位字节排放在内存的高端,高位字节排放在内存的低端。而LITTLE-ENDIAN正好相反。
比如 int a = 0x05060708
在BIG-ENDIAN的情况下存放为:
字节号 0 1 2 3
数据 05 06 07 08
在LITTLE-ENDIAN的情况下存放为:
字节号 0 1 2 3
数据 08 07 06 05
BIG-ENDIAN、LITTLE-ENDIAN跟CPU有关,每一种CPU不是BIG-ENDIAN就是LITTLE-ENDIAN。IA架构的CPU中是Little-Endian,而PowerPC 、SPARC和Motorola处理器则是BIG-ENDIAN。这其实就是所谓的主机 字节序。而网络 字节序是指数据在网络上传输时是大头还是小头的,在Internet的网络字节序是BIG-ENDIAN。所谓的JAVA 字节序指的是在JAVA 虚拟机中多字节类型数据的存放顺序,JAVA字节序也是BIG-ENDIAN。
所以在用C/C++写通信程序时,在发送数据前务必用htonl和htons去把 整型和短整型的数据进行从主机 字节序到网络 字节序的转换,而接收数据后对于整型和短 整型数据则必须调用ntohl和ntohs实现从网络字节序到主机字节序的转换。如果通信的一方是JAVA程序、一方是C/C++程序时,则需要在C/C++一侧使用以上几个方法进行 字节序的转换,而JAVA一侧,则不需要做任何处理,因为JAVA字节序与网络字节序都是BIG-ENDIAN,只要C/C++一侧能正确进行转换即可(发送前从主机序到 网络序,接收时反变换)。如果通信的双方都是JAVA,则根本不用考虑 字节序的问题了。
如果网络上全部是PowerPC,SPARC和Motorola CPU的主机那么不会出现任何问题,但由于实际存在大量的IA架构的CPU,所以经常出现数据传输错误。
文章开头所提出的问题,就是因为程序运行在X86架构的PC SERVER上,发送数据的一端是用C实现的,接收一端是用JAVA实现的,而发送端在发送数据前未进行从主机字节序到网络字节序的转换,这样接收端接收到的是LITTLE-ENDIAN的数据,数据解释自然出错。
具体数据如下,实际发送的数据为23578
发送端发送数据: 1A 5C
接收端接收到数据后,按BIG-ENDIAN进行解释具体数据是多少?你们自己去计算并比较吧!
===============================================================================================
Big Endian and Little Endian
谈到字节序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据 [1]  
为什么要注意 字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略 字节序的存在。但是,如果你的程序要跟别人的程序产生交互呢?尤其是当你把你在微机上运算的结果运用到计算机群上去的话。在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的 0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的 指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。什么?竟然变成另外一个数字了?是的,就是这种后果。因此,在你的C程序传给JAVA程序之前有必要进行 字节序的转换工作。 [1]  
Big-Endian和Little-Endian优缺点
Big-Endian优点:靠首先提取高位字节,你总是可以由看看在偏移位置为0的字节来确定这个数字是正数还是负数。你不必知道这个数值有多长,或者你也不必过一些字节来看这个数值是否含有符号位。这个数值是以它们被打印出来的顺序存放的,所以从二进制到十进制的函数特别有效。因而,对于不同要求的机器,在设计存取方式时就会不同。 [1]  
Little-Endian优点:提取一个,两个,四个或者更长字节数据的汇编指令以与其他所有格式相同的方式进行:首先在偏移地址为0的地方提取最低位的字节,因为地址偏移和字节数是一对一的关系,多重精度的数学函数就相对地容易写了。 [1]  
如果你增加数字的值,你可能在左边增加数字(高位非指数函数需要更多的数字)。因此,经常需要增加两位数字并移动存储器里所有Big-endian顺序的数字,把所有数向右移,这会增加计算机的工作量。不过,使用Little- Endian的存储器中不重要的字节可以存在它原来的位置,新的数可以存在它的右边的高位地址里。这就意味着计算机中的某些计算可以变得更加简单和快速。

无独有偶,所有 网络协议 也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络 字节序 。当两台采用不同 字节序 的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSI C中提供了四个转换 字节序 的宏。

========================================================================================================
/**
* 通信格式转换
*
* Java和一些windows 编程语言如c、c++、delphi所写的网络程序进行通讯时,需要进行相应的转换
* 高、低字节之间的转换
* windows的 字节序为低字节开头
* linux,unix的 字节序为高字节开头
* java则无论平台变化,都是高字节开头
*/
public class FormatTransfer {
/**
* 将int转为低字节在前,高字节在后的byte 数组
* @param n int
* @return byte[]
*/
public static byte[] toLH(int n) {
byte[] b = new byte[4];
b[0] = (byte) (n & 0xff);
b[1] = (byte) (n >> 8 & 0xff);
b[2] = (byte) (n >> 16 & 0xff);
b[3] = (byte) (n >> 24 & 0xff);
return b;
}
/**
* 将int转为高字节在前,低字节在后的byte 数组
* @param n int
* @return byte[]
*/
public static byte[] toHH(int n) {
byte[] b = new byte[4];
b[3] = (byte) (n & 0xff);
b[2] = (byte) (n >> 8 & 0xff);
b[1] = (byte) (n >> 16 & 0xff);
b[0] = (byte) (n >> 24 & 0xff);
return b;
}
/**
* 将short转为低字节在前,高字节在后的byte 数组
* @param n short
* @return byte[]
*/
public static byte[] toLH(short n) {
byte[] b = new byte[2];
b[0] = (byte) (n & 0xff);
b[1] = (byte) (n >> 8 & 0xff);
return b;
}
/**
* 将short转为高字节在前,低字节在后的byte 数组
* @param n short
* @return byte[]
*/
public static byte[] toHH(short n) {
byte[] b = new byte[2];
b[1] = (byte) (n & 0xff);
b[0] = (byte) (n >> 8 & 0xff);
return b;
}
/**
* 将将int转为高字节在前,低字节在后的byte 数组
public static byte[] toHH(int number) {
int temp = number;
byte[] b = new byte[4];
for (int i = b.length - 1; i > -1; i--) {
b = new Integer(temp & 0xff).byteValue();
temp = temp >> 8;
}
return b;
}
public static byte[] IntToByteArray(int i) {
byte[] abyte0 = new byte[4];
abyte0[3] = (byte) (0xff & i);
abyte0[2] = (byte) ((0xff00 & i) >> 8);
abyte0[1] = (byte) ((0xff0000 & i) >> 16);
abyte0[0] = (byte) ((0xff000000 & i) >> 24);
return abyte0;
}
*/
/**
* 将float转为低字节在前,高字节在后的byte 数组
*/
public static byte[] toLH(float f) {
return toLH(Float.floatToRawIntBits(f));
}
/**
* 将float转为高字节在前,低字节在后的byte 数组
*/
public static byte[] toHH(float f) {
return toHH(Float.floatToRawIntBits(f));
}
/**
* 将String转为byte 数组
*/
public static byte[] stringToBytes(String s, int length) {
while (s.getBytes().length < length) {
s += " ";
}
return s.getBytes();
}
/**
* 将 字节数组转换为String
* @param b byte[]
* @return String
*/
public static String bytesToString(byte[] b) {
StringBuffer result = new StringBuffer("");
int length = b.length;
for (int i=0; i<length; i++) {
result.append((char)(b & 0xff));
}
return result.toString();
}
/**
* 将字符串转换为byte 数组
* @param s String
* @return byte[]
*/
public static byte[] stringToBytes(String s) {
return s.getBytes();
}
/**
* 将高 字节数组转换为int
* @param b byte[]
* @return int
*/
public static int hBytesToInt(byte[] b) {
int s = 0;
for (int i = 0; i < 3; i++) {
if (b >= 0) {
s = s + b;
} else {
s = s + 256 + b;
}
s = s * 256;
}
if (b[3] >= 0) {
s = s + b[3];
} else {
s = s + 256 + b[3];
}
return s;
}
/**
* 将低 字节数组转换为int
* @param b byte[]
* @return int
*/
public static int lBytesToInt(byte[] b) {
int s = 0;
for (int i = 0; i < 3; i++) {
if (b[3-i] >= 0) {
s = s + b[3-i];
} else {
s = s + 256 + b[3-i];
}
s = s * 256;
}
if (b[0] >= 0) {
s = s + b[0];
} else {
s = s + 256 + b[0];
}
return s;
}
/**
* 高 字节数组到short的转换
* @param b byte[]
* @return short
*/
public static short hBytesToShort(byte[] b) {
int s = 0;
if (b[0] >= 0) {
s = s + b[0];
} else {
s = s + 256 + b[0];
}
s = s * 256;
if (b[1] >= 0) {
s = s + b[1];
} else {
s = s + 256 + b[1];
}
short result = (short)s;
return result;
}
/**
* 低 字节数组到short的转换
* @param b byte[]
* @return short
*/
public static short lBytesToShort(byte[] b) {
int s = 0;
if (b[1] >= 0) {
s = s + b[1];
} else {
s = s + 256 + b[1];
}
s = s * 256;
if (b[0] >= 0) {
s = s + b[0];
} else {
s = s + 256 + b[0];
}
short result = (short)s;
return result;
}
/**
* 高 字节数组转换为float
* @param b byte[]
* @return float
*/
public static float hBytesToFloat(byte[] b) {
int i = 0;
Float F = new Float(0.0);
i = ((((b[0]&0xff)<<8 | (b[1]&0xff))<<8) | (b[2]&0xff))<<8 | (b[3]&0xff);
return F.intBitsToFloat(i);
}
/**
* 低 字节数组转换为float
* @param b byte[]
* @return float
*/
public static float lBytesToFloat(byte[] b) {
int i = 0;
Float F = new Float(0.0);
i = ((((b[3]&0xff)<<8 | (b[2]&0xff))<<8) | (b[1]&0xff))<<8 | (b[0]&0xff);
return F.intBitsToFloat(i);
}
/**
* 将byte 数组中的元素倒序排列
*/
public static byte[] bytesReverseOrder(byte[] b) {
int length = b.length;
byte[] result = new byte[length];
for(int i=0; i<length; i++) {
result[length-i-1] = b;
}
return result;
}
/**
* 打印byte 数组
*/
public static void printBytes(byte[] bb) {
int length = bb.length;
for (int i=0; i<length; i++) {
System.out.print(bb + " ");
}
System.out.println("");
}
public static void logBytes(byte[] bb) {
int length = bb.length;
String ut = "";
for (int i=0; i<length; i++) {
ut = out + bb + " ";
}
}
/**
* 将int类型的值转换为 字节序颠倒过来对应的int值
* @param i int
* @return int
*/
public static int reverseInt(int i) {
int result = FormatTransfer.hBytesToInt(FormatTransfer.toLH(i));
return result;
}
/**
* 将short类型的值转换为 字节序颠倒过来对应的short值
* @param s short
* @return short
*/
public static short reverseShort(short s) {
short result = FormatTransfer.hBytesToShort(FormatTransfer.toLH(s));
return result;
}
/**
* 将float类型的值转换为 字节序颠倒过来对应的float值
* @param f float
* @return float
*/
public static float reverseFloat(float f) {
float result = FormatTransfer.hBytesToFloat(FormatTransfer.toLH(f));
return result;
}
}

本文转载自:http://blog.csdn.net/caiwenfeng_for_23/article/details/44977637

蜀山下的鱼
粉丝 9
博文 405
码字总数 0
作品 0
广州
高级程序员
私信 提问
C语言网络编程之--字节顺序

C语言网络编程之--字节顺序 字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,各个数据类型在内存中的字节长度可以用sizeof(类型关键字)获得。那么字符是如何存放到内存中的呢?...

wzjxzht
2013/01/10
0
0
C++ htons(), htonl(), ntohs(), ntohl()详解

将多字节整数类型的数据,从主机的字节顺序转化为网络字节顺序 #include <netinet/in.h> uint32t htonl(uint32t hostlong); uint16t htons(uint16t hostshort); uint32t ntohl(uint32t netlo......

cookqq
2013/12/05
8.2K
0
系统平台大小端问题

Internet上的数据以大端方式在网络上传输!!! 常见CPU及大小端 CPU 操作系统 字节顺序 x86 (Intel、AMD等) 所有 little-endian DEC Alpha 所有 little-endian HP-PA NT little-endian HP-PA U...

满小茂
2016/04/02
204
0
大端字节序、小端字节序和网络字节序

一、字节序 字节序是由于不同的主处理器和操作系统,对大于一个字节的变量在内存中的存放顺序不同而产生的。 字节序通常有大端字节序(Big Endian)和小端字节序(Little Endian)两种分类方...

aaron_lyn1985
2017/01/22
0
0
大端小端(Big- Endian和Little-Endian)

字节序(Endian),大端(Big-Endian),小端(Little-Endian) 图文并茂 http://www.cppblog.com/tx7do/archive/2009/01/06/71276.html 在各种计算机体系结构中,对于字节、字等的存储机制有...

AlphaJay
2010/06/10
19.4K
3

没有更多内容

加载失败,请刷新页面

加载更多

只需一步,在Spring Boot中统一Restful API返回值格式与统一处理异常

统一返回值 在前后端分离大行其道的今天,有一个统一的返回值格式不仅能使我们的接口看起来更漂亮,而且还可以使前端可以统一处理很多东西,避免很多问题的产生。 比较通用的返回值格式如下:...

晓月寒丶
昨天
59
0
区块链应用到供应链上的好处和实际案例

区块链可以解决供应链中的很多问题,例如记录以及追踪产品。那么使用区块链应用到各产品供应链上到底有什么好处?猎头悬赏平台解优人才网小编给大家做个简单的分享: 使用区块链的最突出的优...

猎头悬赏平台
昨天
28
0
全世界到底有多少软件开发人员?

埃文斯数据公司(Evans Data Corporation) 2019 最新的统计数据(原文)显示,2018 年全球共有 2300 万软件开发人员,预计到 2019 年底这个数字将达到 2640万,到 2023 年达到 2770万。 而来自...

红薯
昨天
65
0
Go 语言基础—— 通道(channel)

通过通信来共享内存(Java是通过共享内存来通信的) 定义 func service() string {time.Sleep(time.Millisecond * 50)return "Done"}func AsyncService() chan string {retCh := mak......

刘一草
昨天
58
0
Apache Flink 零基础入门(一):基础概念解析

Apache Flink 的定义、架构及原理 Apache Flink 是一个分布式大数据处理引擎,可对有限数据流和无限数据流进行有状态或无状态的计算,能够部署在各种集群环境,对各种规模大小的数据进行快速...

Vincent-Duan
昨天
60
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部