歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Java與C/C++網絡通信時的數據轉換

最近做了一款Android應用,需要和PC端通過socket傳遞數據。PC端是用C++開發的,為了方便傳輸,在發送之前都將數據轉化為字節數組。本以為可以萬事大吉,可是過程還是會有一些問題。下面是我遇到的幾個問題以及解決方案,這裡做一個簡單的記錄,也希望大家遇到後能夠快速解決,同時歡迎各位補充指正~

Java Socket的接收與發送
首先我們會建立一個socket連接,然後從中獲取InputStream和OutputStream。在接收的時候我是用一個DataInputStream對Socket的InputStream進行包裝,便於後面接收不同大小的數據類型。在發送時先通過DataOutputStream()對OutputStream進行包裝,然後將不同的數據類型寫入,最後用ByteArrayOutputStream包裝轉化成字節流,通過已經建立好的Socket通道發送。這樣以來會有以下兩個問題。

問題一:大小端問題
PC端設備使用的是x86平台, 數據為小端格式。而jvm虛擬機采用的大端格式,當你用Java讀取一個字節,如:調用dataInputStream.read()的時候,可以正常的讀出一個字節。但是如果要讀出多字節的數據類型,如:dataInputStream.readInt()的時候,就會出現大小端的數據錯誤。小端格式會把低字節的數據存儲在低地址存儲空間,而大端格式會把低字節的數據存儲在高地址空間。這樣以來,數據的高地位就正好顛倒了。

解決方案
這個問題的解決方案也比較容易,通過對錯誤數據高地位進行蝶形交換可以得到結果。對於大小端格式轉換可以先做一個基於short的轉換,然後對Int數據高低位分別調用short的轉換即可,代碼如下:

    /**
    * 大小端蝶形交換
    *
    * @param data
    *            待交換的short數據
    * @return 轉換後的short數
    */
    private static short swapShortToLittleEndian(short data)
    {
        short ret = (short) ((data << 8) | (data >> 8) & 0x00FF);
        ret = (short) (ret & 0xFFFF);
        return ret;
    }

然後對於int數據可以分別對高低16位進行以上轉換:

    /**
    * 大小端蝶形交換
    *
    * @param data
    *            待交換的int數據
    * @return 轉換後的int數
    */
private static void swapIntToLittleEndian(int data)
    {
        int ret = swapShortToLittleEndian((short) ((data >> 16) & 0xFFFF))
                | (swapShortToLittleEndian((short) data) << 16);
        return ret;
    }

經過以上兩種函數可以將16字節、32字節的數據轉為小端格式,對於64位的可以采用同樣的原理。
PS:可能有朋友會對上面頻繁的和0xFFFF/0xFF作與運算有些疑惑,這裡也是要說的第二個問題,這個與運算是必須的,下面來說說原因

問題二:Java隱式類型轉換
在Java中,對byte 的 + - * / >> >>> << & | ^ (加,減,乘,除,右移,左移,無符號右移,位與,位或,位異或)操作,均會是首先將byte轉化為int, 再行運算。然後,這一過程可能導致多種問題。

解決方案
大家知道在Java中為了方便數據的處理,只存在有符號數。即用最高位來表示符號,其余位表示實際的數據。在上面大小端轉換中,我們需要首先將低8位數據移到高8位,同時將高8位數據移到低8位。在左移的過程當中,右邊毫無疑問是自動補0,那麼直接進行移位的結果會是我們想要的結果。但是如果是右移,對於正數而言(最高位是0),也能得到正確的結果;然而如果是負數(最高位是1),此時右移的時候高位是會自動補1,當然這個可以通過運算符“>>>”來解決,但是這裡還有另一個問題:當對byte移位時,系統會將其隱式轉為int數據,如果是負數,那麼補齊的高位數據則全為1。
如果在右移之後不和0xFF做位與,那麼之後和高位數據位或拼接的時候高位會直接全為1,這也就是為什麼會頻繁做位與操作的原因。

綜上:
1. 用Java接收C++無符號數據的時候,一定要注意大小端的轉換。
2. 另外遇到移位操作,需要注意在右移之後必須將高位清零來消除錯誤。

以上是個人遇到的問題及自己的解決方案,如有更多方案,歡迎補充!

Copyright © Linux教程網 All Rights Reserved