亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

淺談字節序(Byte Order)及其相關操作

系統 2115 0

淺談字節序(Byte Order)及其相關操作 - Jeffrey Zhao - 博客園

淺談字節序(Byte Order)及其相關操作

2010-02-10 23:05 by Jeffrey Zhao, 12152 閱讀, 41 評論, 收藏 , 編輯

最近在為 Tokyo Tyrant 寫一個.NET客戶端類庫。Tokyo Tyrant公開了一個基于TCP協議的二進制協議,于是我們的工作其實也只是按照協議發送和讀取一些二進制數據流而已,并不麻煩。不過在其中涉及到了“字節序”的概念,這本是計算機體系結構/操作系統等課程的基礎,不過我還是打算在這里進行簡單說明,并且對.NET中部分類庫在此類數據流處理時的注意事項進行些許記錄與總結。

字節序(Byte Order)

說到程序間的通信,說到底便是發送數據流。我們一般把字節(byte)看作是數據的最小單位。當然,其實一個字節中還包含8個比特(bit)──有時候我奇怪為什么很多朋友會不知道bit或是它和byte的關系。當我們拿到一系列byte的時候,它本身其實是沒有意義的,有意義的只是“識別字節的方式”。例如,同樣4個字節的數據,我們可以把它看作是1個32位整數、 2個Unicode、 或者字符4個ASCII字符。

同樣我們知道,在一個32位的CPU中“字長”為32個bit,也就是4個byte。在這樣的CPU中,總是以4字節對齊的方式來讀取或寫入內存,那么同樣這4個字節的數據是以什么順序保存在內存中的呢?例如,現在我們要向內存地址為a的地方寫入數據0x0A0B0C0D,那么這4個字節分別落在哪個地址的內存上呢?這就涉及到字節序的問題了。

每個數據都有所謂的“有效位(significant byte)”,它的意思是“表示這個數據所用的字節”。例如一個32位整數,它的有效位就是4個字節。而對于0x0A0B0C0D來說,它的有效位從高到低便是0A、0B、0C及0D——這里您可以把它作為一個256進制的數來看(相對于我們平時所用的10進制數)。

而所謂 大字節序(big endian) ,便是指其“ 最高有效位(most significant byte) ”落在低地址上的存儲方式。例如像地址a寫入0x0A0B0C0D之后,在內存中的數據便是:

Big Endian

而對于 小字節序(little endian) 來說就正好相反了,它把“ 最低有效位(least significant byte) ”放在低地址上。例如:

Little Endian

對于我們常用的CPU架構,如Intel,AMD的CPU使用的都是小字節序,而例如Mac OS以前所使用的Power PC使用的便是大字節序(不過現在Mac OS也使用Intel的CPU了)。此外,除了大字節序和小字節序之外,還有一種很少見的中字節序(middle endian),它會以2143的方式來保存數據(相對于大字節序的1234及小字節序的4321)。

關于字節序的詳細說明,您可以參考Wikipedia里的 Endianness條目

相關.NET類庫

BinaryWriter和BinaryReader

在.NET框架操作數據流的時候,我們往往會使用BinaryWriter和BinaryReader進行讀寫。這兩個類中都有對應的WriteInt32或是ReadInt32方法,那么它們是如何處理字節序的呢?從MSDN上我們了解到 BinaryReader使用小字節序讀取數據 。這意味著:

          
            var 
          
          stream = 
          
            new 
          
          
            MemoryStream
          
          (
          
            new byte
          
          [] { 4, 1, 0, 0 });

          
            var 
          
          reader = 
          
            new 
          
          
            BinaryReader
          
          (stream);

          
            int 
          
          i = reader.ReadInt32(); 
          
            // i == 260
          
        

與之類似,自然BinaryWriter也是使用小字節序來寫入數據。

BitConverter

有時候我們還會使用BitConverter來轉化byte數組及一個32位整數(自然也包括其他類型),這也是涉及到字節序的操作,那么它們又是如何處理的呢?與BinaryWriter和BinaryReader的“固定策略”不同,BitConverter的行為是平臺相關的。

首先,BitConverter有一個只讀靜態字段IsLittleEndian,它表示當前平臺的字節序。由于我們為不同的CPU會安裝不同的.NET類庫,因此您現在如果通過.NET Reflector來查看這個字段會發現它被設置為一個常量true。那么接下來,BitConverter上的各個方便便會根據IsLittleEndian的值產生不同行為了,例如它的ToInt32方法:

          
            public static unsafe int 
          
          ToInt32(
          
            byte
          
          [] value, 
          
            int 
          
          startIndex)
{
    
          
            // ...

    
          
          
            fixed 
          
          (
          
            byte
          
          * numRef = &(value[startIndex]))
    {
        
          
            if 
          
          ((startIndex % 4) == 0)
        {
            
          
            return 
          
          *(((
          
            int
          
          *)numRef));
        }
        
          
            if 
          
          (IsLittleEndian)
        {
            
          
            return 
          
          numRef[0] | (numRef[1] << 8) | (numRef[2] << 16) | (numRef[3] << 24);
        }

        
          
            return 
          
          (numRef[0] << 24) | (numRef[1] << 16) | (numRef[2] << 8) | numRef[3];
    }
}
        

顯然,這里會根據IsLittleEndian返回不同的值。

判斷當前平臺的字節序

在.NET Framework中BitConverter.IsLittleEndian字段是一個常量,也就是說它在編譯期便寫入了一個靜態的值。那么我們如果想要通過代碼來判斷當前平臺的字節序,又該怎么做呢?其實這很簡單:

          
            static unsafe bool 
          
          IsLittleEndian()
{
    
          
            int 
          
          i = 1;
    
          
            byte
          
          * b = (
          
            byte
          
          *)&i;
    
          
            return 
          
          b[0] == 1;
}
        

這里我們通過檢查32位整數1的第一個字節來確定當前平臺的字節序。當然,我們也可以使用其他類型,例如:

          
            static unsafe bool 
          
          AmILittleEndian()
{
    
          
            // binary representations of 1.0:
    // big endian: 3f f0 00 00 00 00 00 00
    // little endian: 00 00 00 00 00 00 f0 3f
    // arm fpa little endian: 00 00 f0 3f 00 00 00 00
    
          
          
            double 
          
          d = 1.0;
    
          
            byte
          
          * b = (
          
            byte
          
          *)&d;
    
          
            return 
          
          (b[0] == 0);
}
        

這段代碼來自mono的BitConverter類庫,至于它為什么使用double而不是int,我也不是很清楚。

Buffer.BlockCopy方法

.NET類庫中自帶一個 Buffer.BlockCopy 方法,它的作用是將一個數組的字節——不是元素——復制到另一個數組中去。換句話說,一個長度為100的int數組經過完整的復制后,就變成了長度為50的long數組,因為一個int為4字節,而long為8字節。從文檔上看,Buffer.BlockCopy是與字節序相關的,也就是說,同樣的.NET代碼在字節序不同的平臺上得到的結果可能不同。因此,我建議在使用這個方法的時候多加小心。

面向特定字節序編程

我們知道,BitConverter的工作結果是和當前平臺的字節序相關的,但是在很多時候,尤其是根據某個公開的協議進行通信編程的時候,是需要固定一個字節序的。例如Tokyo Tyrant便要求每個整數都以大字節序的方式來通信——無論是發送還是讀取。為了保證.NET代碼的平臺無關性,我們不能直接使用BitConverter.GetBytes或ToInt32方法進行轉化。那么我們該怎么辦呢?最直觀的方法自然是手動進行轉換:

          
            static int 
          
          ReadInt32(
          
            Stream 
          
          stream)
{
    
          
            var 
          
          buffer = 
          
            new byte
          
          [4];
    stream.Read(buffer, 0, 4);

    
          
            return 
          
          buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
}
        

由于我們可以通過BitConverter.IsLittleEndian來得到當前平臺的字節序,我們也可以用它進行判斷:

          
            static int 
          
          ReadInt32(
          
            Stream 
          
          stream)
{
    
          
            var 
          
          buffer = 
          
            new byte
          
          [4];
    stream.Read(buffer, 0, 4);

    
          
            if 
          
          (
          
            BitConverter
          
          .IsLittleEndian)
    {
        
          
            Array
          
          .Reverse(buffer);
    }

    
          
            return 
          
          
            BitConverter
          
          .ToInt32(buffer, 0);
}


          
            static void 
          
          WriteInt32(
          
            Stream 
          
          stream, 
          
            int 
          
          value)
{
    
          
            var 
          
          buffer = 
          
            BitConverter
          
          .GetBytes(value);

    
          
            if 
          
          (
          
            BitConverter
          
          .IsLittleEndian)
    {
        
          
            Array
          
          .Reverse(buffer);
    }

    stream.Write(buffer, 0, buffer.Length);
}
        

此外,我們知道BinaryWriter和BinaryReader都是依據小字節序進行讀寫的,因此我們也可以利用這點來讀寫數據流。要不,接下來就由您試試看如何?

淺談字節序(Byte Order)及其相關操作


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 久久精品国产亚洲a不卡 | 精品国产一区二区三区香蕉沈先生 | 一级毛片免费播放视频 | 欧美精品午夜 | 亚洲欧美综合视频 | 亚洲黄a| 国产精品久久亚洲一区二区 | 免费一级毛片清高播放 | a毛片基地| 久久青草免费91观看 | 亚洲国产精久久久久久久春色 | 特黄特色一级aa毛片免费观看 | 在线国产你懂的 | 国产成人啪午夜精品网站男同 | 99国产精品久久久久久久成人热 | 国产日韩高清一区二区三区 | 日本人的色道www免费一区 | 亚洲国产品综合人成综合网站 | 女人牲交视频一级毛片 | 午夜伦情电午夜伦情影院 | 精品免费视在线观看 | 午夜禁片 | 看真人视频一一级毛片 | 久久综合久久美利坚合众国 | 久久久91 | 欧美一级一极性活片免费观看 | 日韩天堂 | 亚洲精品美女一区二区三区乱码 | 91久久夜色精品国产网站 | 亚洲免费久久 | 爱爱免费| 久久伦理片 | 最新日本免费一区二区三区中文 | 一区二区三区免费在线视频 | 国产一级特黄老妇女大片免费 | 夜色成人影院在线www | 欧美黑人巨大肥婆性视频 | 曰批免费视频播放在线看片 | 奇米影视第四色7777 | 亚洲福利影院 | 黄色一级毛片在线观看 |