網絡字節序之大小端(字節序與比特序)

      引言:最近在網上看了很多博客,想要深入瞭解大小端問題,主要是做畢設時,RTP包協議的結構體定義有兩種方式,即大端和小端。但是一些博客並沒有講到理解大小端的本質問題,在這裏按自己的理解擴充一下,可能有錯,望理解!!!

1. 字節序

    字節序即字節的存儲順序,如果數據都是單字節的,那怎麼存儲無所謂了,但是對於多字節數據,比如int,double等,就要考慮存儲的順序了。字節序是硬件層面的東西,通常只和你使用的處理器架構有關,而和編程語言無關。字節序分爲大端序和小端序。

   大端序:數據的高位字節存放在地址的低端 低位字節存放在地址高端。

   小端序:數據的高位字節存放在地址的高端 低位字節存放在地址低端。

2. 比特序

     字節序是一個對象中的多個字節之間的順序問題,比特序就是一個字節中的8個比特位(bit)之間的順序問題。一般情況下系統的比特序和字節序是保持一致的。以二進制數值10110101爲例,其在不同平臺下的內存位序如下:
      大端的含義是數值的最高位1(最左邊的1)放在了內存起始位置上,即數值10110101的大端內存佈局爲10110101。

      小端的含義是數值的最低位1(最右邊的1)放在了內存起始位置上,即數值10110101的小端內存佈局爲10101101。

 3. 位域

      對於位域有一個約定:在C語言的結構體中如果包含了位域,如果位域A定義在位域B之前,那麼位域A總是出現在低地址的比特位。 這就決定了網絡編程中位域在定義時必須處理大小端問題。(同樣,結構體中前面的成員也處於較低的地址)

        對於IP頭,可以這樣理解,網絡傳輸時version在前,ihl在後,網絡是大端序,可以認爲version是數字的高位,ihl是低位,所以:

       在大端機中,由於低地址是高位,所以位域version必須在前面。

       在小端機中,由於高地址是高位,所以位域version必須在後面。

4. RTP頭大小端處理

       如下圖這是網絡大端序要接收數據的格式,RTP協議就是這樣規定的,網絡大端序必須按照這樣的順序發送數據,這樣PC機客戶端(支持RTP協議)接收到數據後,也會按照這樣的格式解析。

        (1)如果你的服務器是大端系統,所以代碼中不需要轉換,按照正常格式定義變量,在內存中也是按照大端模式存儲。

struct _RTP_FIXED_HEADER
{

    unsigned char version:2;        /**//* expect 2 */
    unsigned char padding:1;        /**//* expect 0 */
    unsigned char extension:1;        /**//* expect 1, see RTP_OP below */
    unsigned char csrc_len:4;        /**//* expect 0 */       
    unsigned char marker:1;        /**//* expect 1 */   
    unsigned char payload:7;        /**//* RTP_PAYLOAD_RTSP */
    unsigned short seq_no;        
    unsigned  long timestamp;        
    unsigned long ssrc;            /**//* stream number is used here. */
} __PACKED__;

(2)如果服務器是小端系統,由第二部分比特序可知,我們服務器的代碼最後是在服務器上運行,所以定義的結構體變量會以小端方式存放在內存中,也就是我們需要將結構體的比特位倒序,然後存放在小端系統時,裏面存放的值的順序纔是大端系統需要的,因爲數據的發送與大小端無關,總是從低地址開始發送,直到數據總長度接收。所以我們在存儲的時候就調整好位置,如下代碼所示:

struct _RTP_FIXED_HEADER
{
    /**//* byte 0 */
    unsigned char csrc_len:4;        /**//* expect 0 */
    unsigned char extension:1;        /**//* expect 1, see RTP_OP below */
    unsigned char padding:1;        /**//* expect 0 */
    unsigned char version:2;        /**//* expect 2 */
    /**//* byte 1 */
    unsigned char payload:7;        /**//* RTP_PAYLOAD_RTSP */
    unsigned char marker:1;        /**//* expect 1 */
    /**//* bytes 2, 3 */
    unsigned short seq_no;            
    /**//* bytes 4-7 */
    unsigned  long timestamp;        
    /**//* bytes 8-11 */
    unsigned long ssrc;            /**//* stream number is used here. */
} __PACKED__;

(3)上述代碼中只對比特序進行了調整,這些比特位的組合爲一個字節,爲單字節序,是不需要轉換的,比如我們發送的視頻流數據不需要轉換,因爲他是單字節流。而short與long是多字節序,所以在發送之前需要使用hton(主機轉網絡字節序)函數對多字節進行轉換,代碼如下:

ssrc   = htonl(10); 
seq_no = htons(g_rtspClients[is].seqnum++);

總結:內存中如果有一個存好的數,同樣一個數,他在大端系統和小端系統的代碼中的值卻不一樣,如果代碼中有定義一個數,他在大端小端系統內存中存儲的順序卻不一樣。

5、多字節序

      如下圖:前6個變量爲char,他們以字節爲單位,各自位成員按大小端比較權重。而short爲2字節,該變量本身以2字節爲單位,內部以一字節按大小端比較權重,這纔有了0x1234與0x3412。long爲4字節,4字節爲單位,一字節比較權重,這纔有了0x12345678與0x78563412。

    (1)如果在大小端系統中傳遞數據,所以有了轉換大小端的說法,也出現了像htonl()、htons()這樣的函數去轉換大小端字節序的說法。host to network long/short,字節序的轉換需要我們做。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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