最近在項目開發過程當中,須要在採用JAVA做爲語言的服務器與採用C++做爲語言的服務器間進行通訊,這就涉及到這兩種語言間數據類型的轉換以及網絡字節序與主機字節序的區別。該文主要說說網絡字節序和主機字節序的區別以及Little endian與Big endian的概念。其實編程的事就比較簡單了
我也懶得寫了,直接引用了我以爲寫的挺好的兩篇文章:html
來源:http://blog.ednchina.com/qinyonglyz/194674/message.aspxjava
1.故事的起源linux
「endian」這個詞出自《格列佛遊記》。小人國的內戰就源於吃雞蛋時是究竟從大頭(Big-Endian)敲開仍是從小頭(Little-Endian)敲開,由此曾發生過六次叛亂,其中一個皇帝送了命,另外一個丟了王位。c++
咱們通常將endian翻譯成「字節序」,將big endian和little endian稱做「大尾」和「小尾」。編程
2.什麼是Big Endian和Little Endian?windows
在設計計算機系統的時候,有兩種處理內存中數據的方法。一種叫爲little-endian,存放在內存中最低位的數值是來自數據的最右邊部分(也就是數據的最低位部分)。好比一個16進制數字0x12345678,在內存存放的方式以下:數組
值服務器 |
0111,1000網絡 |
0101,0110app |
0011,0100 |
0001,0010 |
地址 |
100 |
101 |
102 |
103 |
另外一種稱爲big-endian,正好相反,存放在內存中最低位的數值是來自數據的最左邊邊部分(也就是數據的最高爲部分)。好比一個16進制數字0x12345678,在內存存放的方式以下:
值 |
0001,0010 |
0011,0100 |
0101,0110 |
0111,1000 |
地址 |
100 |
101 |
102 |
103 |
好比某些文件須要在不一樣平臺處理,或者經過Socket通訊。這方面咱們能夠藉助ntohl(), ntohs(), htonl(), and htons()函數進行格式轉換。
3.如何判斷系統是Big Endian仍是Little Endian?
在/usr/include/中(包括子目錄)查找字符串BYTE_ORDER(或_BYTE_ORDER, __BYTE_ORDER),肯定其值。這個值通常在endian.h或machine/endian.h文件中能夠找到,有時在feature.h中,不一樣的操做系統可能有所不一樣。通常來講,Little Endian系統BYTE_ORDER(或_BYTE_ORDER,__BYTE_ORDER)爲1234,Big Endian系統爲4321。大部分用戶的操做系統(如windows, FreeBsd,Linux)是Little Endian的。少部分,如MAC OS ,是Big Endian 的。本質上說,Little Endian仍是Big Endian與操做系統和芯片類型都有關係。
======================================================================
ext3 文件系統在硬盤分區上的數據是按照 Intel 的 Little-endian 格式存放的,若是是在 PC 之外的平臺上開發 ext3 相關的程序,要特別注意這一點。
談到字節序的問題,必然牽涉到兩大 CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列採用big endian方式存儲數據,而x86系列則採用little endian方式存儲數據。那麼究竟什麼是big endian,什麼又是little endian呢?
其實big endian是指低地址存放最高有效字節(MSB),而little endian則是低地址存放最低有效字節(LSB)。
用文字說明可能比較抽象,下面用圖像加以說明。好比數字0x12345678在兩種不一樣字節序CPU中的存儲順序以下所示:
Big Endian
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12 | 34 | 56 | 78 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Little Endian
低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 78 | 56 | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
從上面兩圖能夠看出,採用big endian方式存儲數據是符合咱們人類的思惟習慣的。而little endian,!@#$%^&*,見鬼去吧 -_-|||
爲何要注意字節序的問題呢?你可能這麼問。固然,若是你寫的程序只在單機環境下面運行,而且不和別人的程序打交道,那麼你徹底能夠忽略字節序的存在。可是,若是你的程序要跟別人的程序產生交互呢?在這裏我想說說兩種語言。C/C++語言編寫的程序裏數據存儲順序是跟編譯平臺所在的CPU相關的,而 JAVA編寫的程序則惟一採用big endian方式來存儲數據。試想,若是你用C/C++語言在x86平臺下編寫的程序跟別人的JAVA程序互通時會產生什麼結果?就拿上面的 0x12345678來講,你的程序傳遞給別人的一個數據,將指向0x12345678的指針傳給了JAVA程序,因爲JAVA採起big endian方式存儲數據,很天然的它會將你的數據翻譯爲0x78563412。什麼?居然變成另一個數字了?是的,就是這種後果。所以,在你的C程序傳給JAVA程序以前有必要進行字節序的轉換工做。
無獨有偶,全部網絡協議也都是採用big endian的方式來傳輸數據的。因此有時咱們也會把big endian方式稱之爲網絡字節序。當兩臺採用不一樣字節序的主機通訊時,在發送數據以前都必須通過字節序的轉換成爲網絡字節序後再進行傳輸。
來源:http://www.cnblogs.com/jacktu/archive/2008/11/24/1339789.html
不一樣的CPU有不一樣的字節序類型 這些字節序是指整數在內存中保存的順序 這個叫作主機序
最多見的有兩種
1. Little endian:將低序字節存儲在起始地址
2. Big endian:將高序字節存儲在起始地址
LE little-endian
最符合人的思惟的字節序
地址低位存儲值的低位
地址高位存儲值的高位
怎麼講是最符合人的思惟的字節序,是由於從人的第一觀感來講
低位值小,就應該放在內存地址小的地方,也即內存地址低位
反之,高位值就應該放在內存地址大的地方,也即內存地址高位
BE big-endian
最直觀的字節序
地址低位存儲值的高位
地址高位存儲值的低位
爲何說直觀,不要考慮對應關係
只須要把內存地址從左到右按照由低到高的順序寫出
把值按照一般的高位到低位的順序寫出
二者對照,一個字節一個字節的填充進去
例子:在內存中雙字0x01020304(DWORD)的存儲方式
內存地址
4000 4001 4002 4003
LE 04 03 02 01
BE 01 02 03 04
例子:若是咱們將0x1234abcd寫入到以0x0000開始的內存中,則結果爲
big-endian little-endian
0x0000 0x12 0xcd
0x0001 0x23 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
x86系列CPU都是little-endian的字節序.
網絡字節順序是TCP/IP中規定好的一種數據表示格式,它與具體的CPU類型、操做系統等無關,從而能夠保證數據在不一樣主機之間傳輸時可以被正確解釋。網絡字節順序採用big endian排序方式。
爲了進行轉換 bsd socket提供了轉換的函數 有下面四個
htons 把unsigned short類型從主機序轉換到網絡序
htonl 把unsigned long類型從主機序轉換到網絡序
ntohs 把unsigned short類型從網絡序轉換到主機序
ntohl 把unsigned long類型從網絡序轉換到主機序
在使用little endian的系統中 這些函數會把字節序進行轉換
在使用big endian類型的系統中 這些函數會定義成空宏
一樣 在網絡程序開發時 或是跨平臺開發時 也應該注意保證只用一種字節序 否則兩方的解釋不同就會產生bug.
注:
一、網絡與主機字節轉換函數:htons ntohs htonl ntohl (s 就是short l是long h是host n是network)
二、不一樣的CPU上運行不一樣的操做系統,字節序也是不一樣的,參見下表。
處理器 操做系統 字節排序
Alpha 所有 Little endian
HP-PA NT Little endian
HP-PA UNIX Big endian
Intelx86 所有 Little endian <-----x86系統是小端字節序系統
Motorola680x() 所有 Big endian
MIPS NT Little endian
MIPS UNIX Big endian
PowerPC NT Little endian
PowerPC 非NT Big endian <-----PPC系統是大端字節序系統
RS/6000 UNIX Big endian
SPARC UNIX Big endian
IXP1200 ARM核心 所有 Little endian
=============================================
字節序轉換類:
/**
* 通訊格式轉換
*
* 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;
}
}