網絡字節序和大小端字節序


http://blog.csdn.net/luotuo44/article/details/19234553

 

先說說為什么會有大小端字節序的問題。現在PC機的一個整型變量一般是32位的,由4個字節組成。在計算機內存中,每個字節都是有地址的。也就是說一個整型的4個字節的地址是不同的,有高低地址之分。對於一個整數,如632523,其對應的二進制位1001 10100110 11001011。需要3個字節才能放得下。這時就存在一個問題,對於低8位11001011是存放在整型的那4個字節的低地址位還是高地址位。

        如果將低8位存放在4個字節中的低地址位,稱為小端字節序,如果將低8位存放在高地址位,則為大端字節序。助記:沿着內存的增長方向,先存低8位是的小端;先存高8位的是大端。大小端字節序是由CPU決定的[1][2][3]

        雖然不同的CPU廠商可以隨意選擇一種字節序作為自己的內存字節序,但是網絡字節序就不能任由各個CPU選擇,網絡字節序被規定為大端字節序

        一般來說,主機要先把端口號從主機字節序轉換到網絡字節序。有下面的函數可以相互轉換。

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. #include<netinet/in.h>  
  2. unsigned long int htonl(unsigned long int hostlong);  
  3. unsigned short int htons(unsigned short int hostshort);  
  4. unsigned long int ntohl(unsigned long int netlong);  
  5. unsigned short int ntohs(unsigned short int netshort);  

 

        其中,htonl 表示”host to network long”。

        有了這些轉換函數,用戶就不需要知道所在的OS/CPU用的什么字節序。反正這些函數會被我們想要的值轉換成網絡字節序的值。

 

        除了這4個函數外,在頭文件endian.h中,還定義了很多主機字節序和大小端字節序相互轉換的函數。

        從這里可以看到,下面函數是在glibc 2.9及之后的版本才支持。可以用$ldd --version命令 查看glibc的版本。 

       

 

       其中,be是表示大端,le是表示小端

 

       上圖中的那些函數(其實那些是宏定義),用來配置端口號是沒有用的,因為已經有htons函數了。但在解決TCP粘包問題中,這些函數就可以派上用場了。

        解決TCP粘包問題的一個方法是,在每條消息的頭部添加一個長度字段。每一個主機在發送消息前,都把長度轉換成大端字節序存放(轉換成小端字節序也行,只要CS兩端統一即可)。然后發送消息。對端在收到消息的長度字段后,就將其從大端字節序轉換成主機字節序。

       這里存在一個問題,怎么將怎么將收到的字節流轉換成大端字節序的整型變量。答案是用memcpy函數。在發送端將整型變量變成字節流也是用這個函數。

 

       如下面代碼所示:

 

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1.  //發送端  
  2.  int len = htobe32(a);  
  3.  char ch[10];  
  4.  memcpy(ch, &len, sizeof(len) );  
  5.   
  6.    
[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. //接收端  
  2. int len;  
  3. char* message;  
  4. memcpy(&len, message, sizeof(len) );  
  5. a = be32toh(len);  

 

CDataStream里的實現是通過賦值運算實現的:

void writeint32(int32 iValue)
{
  assert((current_ + sizeof(int32)) <= (buffer_ + size_));
  if((current_ + sizeof(int32)) <= (buffer_ + size_))
  {  
    *((int32*)current_) = iValue;
    current_ +=sizeof(int32);
  }
  else
    good_bit_ = false;
}

 

int readint32()
{
  assert((current_ + sizeof(int32)) <= (buffer_ + size_));
  if((current_ + sizeof(int32)) <= (buffer_ + size_))
  {
    current_ +=sizeof(int32);
    return *((int32*)(current_-sizeof(int32)));
  }
  good_bit_ = false;
  return 0;

}

 

不過它沒有處理網絡字節序和主機字節序

 

 

 

參考:

[1]  http://en.wikipedia.org/wiki/Endianness

[2] http://stackoverflow.com/questions/9237317/what-makes-a-system-little-endian-or-big-endian

[3] http://superuser.com/questions/308274/what-determines-endianness


免責聲明!

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



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