java與 C++ 之間進行 SOCKET 通信要點簡要解析

 


一、 big-endian  little-endian

   Endian定義: 在計算機系統體系結構中用來描述在多字節數中各個字節的存儲順序。 java

big-endian也稱高位在前、大端在前。是 計算機體系結構中一種描述多字節存儲順序的術語,在這種機制中最重要字節(MSB )存放在最低端的地址 上。採用這種機制的處理器有Mortolora  PowerPC 微處理器系列和絕大多數的 RISC 處理器。 ios

big-endian 最直觀的字節序: 程序員

內存地址從左到右與值由低到高的順序相對應。 數組

little-endian也稱低位在前、小端在前。 計算機體系結構中一種描述多字節存儲順序的術語,在這種機 制中最不重要字節(LSB )存放在最低端的地 址上。採用這種機制的處理器有 Intel x86 系列微處理器和一些網絡通訊設備。該術語除了描述多字節存儲順序外還經常用來描述一個字節中各個比特的排放次序 ,這裏僅討論多字節存儲循序  服務器

little-endian是最符合人的思惟的字節序,低與低,高與高一一對應: 網絡

地址低位存儲值的低位  socket

地址高位存儲值的高位 函數

  

     下面舉一個例子具體說明 big-endian  little-endian: 編碼

      int  nValue = 0x01020304; spa

      上面的整型nValue 有 4 個字節,其中 01 爲最高位的字節, 04 爲最低位的字節。那麼在內存(或文件)中,該值的存儲循序爲:

      內存(或文件)地址:0x12000001  0x12000002  0x12000003  0x12000004  

      Big-endian         :      01         02           03         04

      Little-endian        :      04         03           02         01

    

      若是用一個byte 數組來保存的話,也就是以下:

      Big-endian模式下:  byte  byValue[] = {0x01, 0x02, 0x03, 0x04};

      Little-endian模式下: byte  byValue[] = {0x04, 0x03, 0x02, 0x01};

Big-endian 或是 little-endian的判斷:

bool   IsLittleEndian ()

{

       int     i       = 1;  

       char  * p       = ( char *)& i ;  

   i f    (   * p    =   1   )            

          return   true ;    // 小端 

   else

  return   false ;   // 大端

}


二、網絡字節序與主機字節序

在各類計算機體系結構中,對於字節、字等的存儲機制有所不一樣,於是引起了計算機通訊領域中一個很重要的問題,即通訊雙方交流的信息單元(比特、字節、字、雙字等等)應該以什麼樣的順序進行傳送。若是不達成一致的規則, 通訊雙方 將沒法進行正確的編/ 譯碼從而致使通訊失敗。

一般所說的網絡字節序(Network Byte Order )就是遵循 big-endian 規則。實際通訊過程當中,通訊雙方須要把數據按照 big-endian 編碼再經過網絡傳輸。

一般所說的主機字節序(Host Byte Order ),與 CPU 的字節序一致。 x86 系列主機的字節序都是 little-endian 桂冊。全部 little-endian 規則主機直接經過網絡通信的時候,須要進行字節序轉化。

    爲了進行轉換 bsd socket 提供了轉換的函數 有下面四個

 htons 把 unsigned short 類型從主機 字節 序轉換到 網絡字節序

 htonl 把 unsigned long 類型從主機 字節 序轉換到 網絡字節序

 ntohs 把 unsigned short 類型從 網絡字節序 轉換到主機 字節 

     ntohl 把 unsigned long 類型從網絡 字節 序轉換到主機 字節 

    在使用little endian 的系統中這些函數會把字節序進行轉換

    在使用big endian 類型的系統中這些函數會定義成空宏


三、 java 字節序

因爲Java 運行須要本身的虛擬機來支持,因此 Java 程序所支持的字節序與 Java 虛擬機一致。Java 虛擬機遵循的是 big-endian 規則。因此能夠把 Java 字節序看做是遵循 big-endian 規則的主機字節序。


四、 Java 程序與 C++ 之間的 SOCKET 通信

4.1 字節序問題

一直以來都在進行着C++ 上面的網絡開發,發如今 C++ 上面進行通信的時候,基本上都沒有考慮到網絡字節序的問題,特別是網絡應用中的用戶數據。你們都知道網絡通信傳輸的都是字節流的數據,因而都是定義一個 char 類型的緩衝區,而後無論 int , WORD, DWORD 仍是其餘自定義類型的結構對象也好,都直接 memcpy() 拷貝到緩衝區,直接發送出去了,根本都沒有考慮所傳輸數據的網絡字節序問題。若是非要說一點關注了網絡字節序問題的話,那就是有一個地方,你們都回去用到的,也就是網絡通信端口,把端口號賦值給 sockaddr_in .sin_port之時你們都會使用了htons() ,也就是把端口號從主機字節序轉化爲網絡字節序。

由於這些程序的服務器端也好,客戶端也好,都是在x86 系列系統下運行,而且都是 C++ 編譯出來的,因此不會由於字節序而出現問題。

如今所作項目,涉及到Java 與 C++ 之間的 SOCKET 通信,這樣的狀況下,就須要你們都按規則來辦事了, C++ 方面傳輸的多字節類型數據還請從主機字節序轉化成網絡字節序再進行傳輸。

固然,數據是由程序員來組合的,也是由程序員來解析的,因此若是不按標準行事也是能夠的。可是就須要在解析數據的時候注意好了。

建議仍是按標準行事,把數據轉化成網絡字節序。

     PS:

     Java與 Windows 平臺下其餘開發語言之間進行數據交與,也須要遵循該原則;

     Java下讀寫 Windows 平臺下其餘開發語言保存的數據,或是 Windows 平臺下其餘語言讀寫Java 保存的數據,也須要注意字節序問題。


4.2 字節對齊問題

#include   <iostream>

using   namespace   std ;

typedef   struct   tag_S1

{

     char   s_szValue [8];

     char   s_cValue ;

 S1 ;

typedef   struct   tag_S2

{

     int    s_nValue1 ;

     char   s_szValue [8];

     char   s_cValue ;

     int    s_nValue2 ;

 S2 ;

typedef   struct   tag_S3

{

     int    s_nValue ;

     char   s_cValue ;

 S3 ;

#pragma   pack ( push , 1)

typedef   struct   tag_S4

{

     int    s_nValue ;

     char   s_cValue ;

 S4 ;

#pragma   pack ( pop )

int   main ( int   argc  char  argv [])

{   

     cout  <<  "sizeof(S1):"  <<  sizeof ( S1 ) <<  endl ;

     cout  <<  "sizeof(S2):"  <<  sizeof ( S2 ) <<  endl ;

     cout  <<  "sizeof(S3):"  <<  sizeof ( S3 ) <<  endl ;

     cout  <<  "sizeof(S4):"  <<  sizeof ( S4 ) <<  endl ;

     system ( "pause" );

     return  0;

}

上面的程序在 WinXP sp3 + VS2008Sp1下運行結果以下:

sizeof(S1):9

sizeof(S2):20

sizeof(S3):8

sizeof(S4):5

請按任意鍵繼續. . .

Win32位平臺下的微軟 C 編譯器 (cl.exe for 80x86) 的對齊策略:

1) 結構體變量的首地址可以被其最寬基本類型成員的大小所整除;

備註:編譯器在給結構體開闢空間時,首先找到結構體中最寬的基本數據類型,而後尋找內存地址能被該基本數據類型所整除的位置,做爲結構體的首地址。將這個最寬的基本數據類型的大小做爲上面介紹的對齊模數。

2) 結構體每一個成員相對於結構體首地址的偏移量( offset )都是成員大小的整數倍,若有須要編譯器會在成員之間加上填充字節( internal adding );

備註: 爲結構體的一個成員開闢空間以前,編譯器首先檢查預開闢空間的首地址相對於結構體首地址的偏移是不是本成員的整數倍,如果,則存放本成員,反之,則在本成員和上一個成員之間填充必定的字節,以達到整數倍的要求,也就是將預開闢空間的首地址後移幾個字節。

3) 結構體的總大小爲結構體最寬基本類型成員大小的整數倍,若有須要,編譯器會在最末一個成員以後加上填充字節( trailing padding )。

備註:結構體總大小是包括填充字節,最後一個成員知足上面兩條之外,還必須知足第三條,不然就必須在最後填充幾個字節以達到本條要求。   

    Windows 32位系統下, VC 中,默認的字節對齊方式是 4 字節對齊。

sizeof(S1) :由於結構中數據類型都是 char ,最寬基本類型大小是 1 ,因此結構大小爲 9, S1 沒有進行填充;

sizeof(S2)、 sizeof(S3) : S2 , S3 就被填充了必定的字節;

sizeof(S4):由於設置了對齊方式爲 1 字節對齊,因此不會被填充。

Java 與 C++ 進行 SOCKET 通信的 C++ 端程序,建議涉及網絡通信的結構使用 1 字節對齊方式,否則 Java 端會增長數據處理的複雜度。


4.3 Java與 C++ 之間基本數據類型的差異

須要注意如下幾個數據類型的區別(32 位系統下 ) :

      C++                       Java

  char---------1byte          Byte----------1byte

                              Char----------2byte2

  long---------4bytes         long----------8bytes

注意:

Java中的 Char 是一個字符,而不是一個字節,與 VC 的 WORD 長度一致;

Java中的 Byte 是一個字節,與 C++ 中的 char 含義一致,而 VC 中的 BYTE 是無符號的char ;

Java中的 long 長度爲 8 ,而 VC 中的 long 長度爲 4 ( C++ 中 short , long 的長度跟編譯器的實現相關)。

相關文章
相關標籤/搜索