詳解C語言的htons和htonl函數、大尾端、小尾端


在Linux和Windows網絡編程時需要用到htons和htonl函數,用來將主機字節順序轉換為網絡字節順序。

     在Intel機器下,執行以下程序

 

int main()
{
   printf("%d /n",htons(16));
      return 0;
}

得到的結果是4096,初一看感覺很怪。

    解釋如下,數字16的16進制表示為0x0010,數字4096的16進制表示為0x1000。 由於Intel機器是小尾端,存儲數字16時實際順序為1000,存儲4096時實際順序為0010。因此在發送網絡包時為了報文中數據為0010,需要經過htons進行字節轉換。如果用IBM等大尾端機器,則沒有這種字節順序轉換,但為了程序的可移植性,也最好用這個函數。

   另外用注意,數字所占位數小於或等於一個字節(8 bits)時,不要用htons轉換。這是因為對於主機來說,大小尾端的最小單位為字節(byte)。

什么是大端模式(big-endian),為什么使用大端模式(big-endian)。

uint16_t htons(uint16_t hostshort);
  htons的功能:將一個無符號短整型數值轉換為網絡字節序,即大端模式(big-endian)
  參數u_short hostshort: 16位無符號整數
  返回值:TCP / IP網絡字節順序
  htons 是把你機器上的整數轉換成“網絡字節序”, 網絡字節序是 big-endian,也就是整數的高位字節存放在內存的低地址處。 而我們常用的 x86 CPU (intel, AMD) 電腦是 little-endian,也就是整數的低位字節放在內存的低字節處。舉個例子吧。假定你的port是0x1234,在網絡字節序里 這個port放到內存中就應該顯示成addr addr+1,也就是:0x12 0x34;而在x86電腦上,0x1234放到內存中實際是:addr addr+1,也就是:0x34 0x12。htons 的用處就是把實際內存中的整數存放方式調整成“網絡字節序”的方式。

第一個問題:為什么使用兩個字節,也就是16位來存儲。

  這個簡單一些,因為一個字節只能存儲8位2進制數,而計算機的端口數量是65536個,也就是2^16,兩個字節。

第二個為題:為什么計算機需要大端模式和小端模式?

  小端模式 :強制轉換數據不需要調整字節內容,1、2、4字節的存儲方式一樣。
  大端模式 :符號位的判定固定為第一個字節,容易判斷正負。

    big endian:大尾端,也稱大端(高位)優先存儲。
    little endian:小尾端,也稱小端(低位)優先存儲。
 
   如下00000000 00000000 00000000 00000001的存儲
       
    大尾端:  00000000 00000000 00000000 00000001
           addr+0    addr+1     addr+2   addr+3     //先存高有效位(在低地址)
  
   小尾端:  00000001 00000000 00000000 00000000
           addr+0    addr+1     addr+2   addr+3     //先存低有效位(在低地址)

 

 

 

   故要判斷機器的體系結構是大尾端還是小尾端,以下程序可以完成任務:
  
   

#include <stdio.h>
int main()
{
    int tt = 1;
    char *c = (char*)(&tt);
    if(*c == 1)
    {
        printf("litte endian\n");
    }
    else
    {
        printf("big endian\n");
    }
    return 0;
}

 

大小尾端數據間的相互轉換

/*
      usage: to convert between the form of big-endian and little-endian
      author: ydzhang
      date: 2008年12月6日20:23:48
*/
#include <stdio.h>
typedef unsigned int u32;
typedef unsigned short u16;

#define BSWAP_16(x) \
        (u16) ( ((((u16)(x) & 0x00ff)) << 8) \
        | (((u16)(x) & 0xff00) >> 8) )

u16 bswap_16(u16 x)
{
    return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8);
}

u32 bswap_32(u32 x)
{
    return ((x & 0x000000ff) << 24) |
           ((x & 0x0000ff00) << 8) |
           ((x & 0x00ff0000) >> 8) |
           ((x & 0xff000000) >> 24);
}

int main()
{
     u16 num_16 = 0x1234;
     u32 num_32 = 0x12345678;

    printf("%x\n", bswap_16(num_16));
    printf("%x\n", BSWAP_16(num_16));
    printf("%x\n", bswap_32(num_32));
    return 0;
}
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM