2009-02-06 109 views
5

我通過網絡傳輸一個字符/字節數組。它包含一個頭文件和一些數據。我想將頭部映射到一個結構體上。這裏是一個例子:將一個字節數組反序列化爲一個結構

#pragma pack(1) 

struct Header 
{ 
    unsigned short bodyLength; 
    int msgID; 
    unsigned short someOtherValue; 
    unsigned short protocolVersion; 
}; 

int main() 
{ 
    boost::array<char, 128> msgBuffer; 
    Header header; 

    for(int x = 0; x < sizeof(Header); x++) 
     msgBuffer[x] = 0x01; // assign some values 

    memcpy(&header, msgBuffer.data(), sizeof(Header)); 

    system("PAUSE");  

    return 0; 
} 

這將始終工作假設結構從不包含任何可變長度字段?有沒有一個平臺獨立/慣用的方式做到這一點?

注:

我所看到的,讓你序列化/反序列化在互聯網上相當多的庫,但我得到的印象是,如果它已經奔先前相同的庫序列化,他們只能反序列化的東西。那麼,我無法控制傳輸的格式。我肯定會得到一個字節/字符數組,其中所有的值都會緊跟在一起。

回答

5

某些處理器要求某些類型正確對齊。他們不會接受指定的包裝併產生硬件陷阱。

即使在常見的x86打包結構中,也會導致代碼運行速度變慢。

另外你在使用不同的排序平臺時必須小心。順便說一句,如果你想要一個簡單的平臺無關的通信機制並綁定到許多編程語言,那麼看看YAMI

5

只是簡單的複製很可能會破壞,至少如果數據可能來自不同的體系結構(甚至只是編譯器)而不是您所在的目錄。這是原因:

這第二個環節是GCC特有的,而是適用於所有的編譯器。

我推薦閱讀字節逐字節,並從這些字節組裝更大的字段(整數等)。這使您可以控制字節序和填充。

2

#pragma pack(1)指令應該在大多數編譯器的工作,但你可以(在你的情況,如果10我的數學是正確的)工作了你的數據結構應該多大,並使用printf("%d", sizeof(Header));檢查包裝正在做檢查。

正如其他人所說,如果你要在不同的架構之間進行操作,你仍然需要警惕Endianness。

0

我知道我與誰交流,所以我不必擔心排序。但是我喜歡遠離編譯器特定的命令。

那麼這個怎麼樣:

const int kHeaderSizeInBytes = 6; 

struct Header 
{ 
    unsigned short bodyLength; 
    unsigned short msgID; 
    unsigned short protocolVersion; 

    unsigned short convertUnsignedShort(char inputArray[sizeof(unsigned short)]) 
     {return (((unsigned char) (inputArray[0])) << 8) + (unsigned char)(inputArray[1]);} 

    void operator<<(char inputArray[kHeaderSizeInBytes]) 
    { 
     bodyLength = convertUnsignedShort(inputArray); 
     msgID = convertUnsignedShort(inputArray + sizeof(bodyLength)); 
     protocolVersion = convertUnsignedShort(inputArray + sizeof(bodyLength) + sizeof(msgID)); 
    } 
}; 

int main() 
{ 
    boost::array<char, 128> msgBuffer; 
    Header header; 

    for(int x = 0; x < kHeaderSizeInBytes; x++) 
     msgBuffer[x] = x; 

    header << msgBuffer.data(); 

    system("PAUSE");  

    return 0; 
} 

擺脫了編譯的,但它並不像我想爲通用。每次向標題添加字段時,都必須修改< <函數。你能否以某種方式遍歷結構字段,獲取字段的類型並調用相應的函數?

+0

關於迭代結構字段:您是否必須使用結構?我在問,因爲用元組替換它將允許迭代字段。 – 2009-02-06 14:05:24

1

我堅決不同意逐字節讀取的想法。如果你在結構聲明中關心結構打包,你可以毫無問題地複製到結構中。對於endiannes問題,再次逐字節讀取解決了這個問題,但並沒有給你一個通用的解決方案。這種方法非常蹩腳。之前我做過類似的工作,它的工作沒有問題。

想想這個。我有一個結構,我也有相應的結構定義。你可以手工構造這個,但我已經爲此寫了一個解析器,並將它用於其他事情。

例如,上面給出的結構的定義是「s s s s」。 (s = short,i = int)然後,我將結構地址,這個結構的定義和結構打包選項賦予一個處理endiannes事物的特殊函數,並且它完成了。

SwitchEndianToBig(& header,「s i s s」,4); // 4 =結構包裝選項

1

告訴我,如果我錯了,但據我所知,做這種方式將保證你的數據是正確的 - 假設類型具有相同尺寸上的不同的平臺

#include <array> 
#include <algorithm> 

//#pragma pack(1) // not needed 

struct Header 
{ 
    unsigned short bodyLength; 
    int msgID; 
    unsigned short someOtherValue; 
    unsigned short protocolVersion; 
    float testFloat; 

    Header() : bodyLength(42), msgID(34), someOtherValue(66), protocolVersion(69), testFloat(3.14f) {} 
}; 

int main() 
{ 
    std::tr1::array<char, 128> msgBuffer; 
    Header header; 

    const char* rawData = reinterpret_cast< const char* >(&header); 

    std::copy(rawData, rawData + sizeof(Header), msgBuffer.data()); // assuming msgBuffer is always big enough 

    system("PAUSE");  

    return 0; 
} 

如果目標平臺上的類型不同,則必須對每種類型使用別名(typedef)以確保每種類型的大小相同。

+0

嗯,你會採取另一種方式(將結構轉換爲字節數組)。而且即使這樣也不會像應該那樣工作,因爲您正在將結構的填充複製到數組中。 – drby 2009-02-09 08:29:15

相關問題