BIG-ENDIAN(大位元組序、高位元組序)
LITTLE-ENDIAN(小位元組序、低位元組序)
主機位元組序
網路位元組順序
JAVA位元組序
1.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
2.BIG-ENDIAN、LITTLE-ENDIAN、跟CPU有關的,每一種CPU不是BIG-ENDIAN就是LITTLE-ENDIAN、。IA架構的CPU中是Little-Endian,而PowerPC 、SPARC和Motorola處理器。這其實就是所謂的主機位元組序。而網路位元組序是指資料在網路上傳輸時是大頭還是小頭的,在Internet的網路位元組序是BIG-ENDIAN。所謂的JAVA位元組序指的是在JAVA虛擬機器中多位元組類型資料的存放順序,JAVA位元組序也是BIG-ENDIAN。
3.所以在用C/C++寫通訊程式時,在發送資料前務必用htonl和htons去把整型和短整型的資料進行從主機位元組序到網路位元組序的轉換,而接收資料後對於整型和短整型資料則必須調用ntohl和ntohs實現從網路位元組序到主機位元組序的轉換。如果通訊的一方是JAVA程式、一方是C/C++程式時,則需要在C/C++一側使用以上幾個方法進行位元組序的轉換,而JAVA一側,則不需要做任何處理,因為JAVA位元組序與網路位元組序都是BIG-ENDIAN,只要C/C++一側能正確進行轉換即可(發送前從主機序到網路序,接收時反變換)。如果通訊的雙方都是JAVA,則根本不用考慮位元組序的問題了。
4.如果網路上全部是PowerPC,SPARC和Motorola CPU的主機那麼不會出現任何問題,但由於實際存在大量的IA架構的CPU,所以經常出現資料轉送錯誤。
5.文章開頭所提出的問題,就是因為程式運行在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方式儲存資料。那麼究竟什麼是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方式稱之為網路位元組序。當兩台採用不同位元組序的主機通訊時,在發送資料之前都必須經過位元組序的轉換成為網路位元組序後再進行傳輸。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 out = ""; for (int i=0; i<length; i++) { out = 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;} }