網絡字節序與主機字節序

網絡字節序和主機字節序的區別以及Little endian與Big endian的概念。

 

什麼是Big Endian和Little Endian?

來源:http://blog.ednchina.com/qinyonglyz/194674/message.aspx

1.故事的起源

“endian”這個詞出自《格列佛遊記》。小人國的內戰就源於吃雞蛋時是究竟從大頭(Big-Endian)敲開還是從小頭(Little- Endian)敲開,由此曾發生過六次叛亂,其中一個皇帝送了命,另一個丟了王位。

我們一般將endian翻譯成“字節序”,將big endian和little endian稱作“大尾”和“小尾”。

2.什麼是Big Endian和Little Endian?

在設計計算機系統的時候,有兩種處理內存中數據的方法。一種叫爲little-endian,存放在內存中最低位的數值是來自數據的最右邊部分(也 就是數據的最低位部分)。比如一個16進制數字0x12345678,在內存存放的方式如下:

0111,1000

0101,0110

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.

注:
1、網絡與主機字節轉換函數:htons ntohs htonl ntohl (s 就是short l是long h是host n是network)
2、不同的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;
}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章