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;
}

假设结构从不包含任何可变长度字段,这是否总是有效?是否有独立于平台/惯用的方式来做到这一点?

笔记:

我在互联网上看到了很多可以让你序列化/反序列化的库,但我的印象是,它们只能反序列化之前已经用同一个库序列化的东西。好吧,我无法控制传输的格式。我肯定会得到一个字节/字符数组,其中所有值都相互跟随。

4

6 回答 6

5

一些处理器要求某些类型正确对齐。他们将不接受指定的打包并生成硬件陷阱。

甚至在常见的 x86 打包结构上也会导致代码运行更慢。

此外,在使用不同的字节序平台时,您必须小心。

顺便说一句,如果你想要一个简单且独立于平台的通信机制,并绑定到许多编程语言,那么看看YAMI

于 2009-02-06T11:30:35.020 回答
5

只是简单的复制很可能会破坏,至少如果数据可以来自与您所在的架构不同的架构(甚至只是编译器)。这是因为:

第二个链接是 GCC 特定的,但这适用于所有编译器。

我建议逐字节读取字段,并从这些字节中组装更大的字段(整数等)。这使您可以控制字节顺序和填充。

于 2009-02-06T11:35:25.367 回答
2

#pragma pack(1)指令应该适用于大多数编译器,但您可以通过计算出您的数据结构应该有多大(如果我的数学正确,则为 10)并使用printf("%d", sizeof(Header));来检查打包是否完成来检查。

正如其他人所说,如果您在架构之间进行切换,您仍然需要警惕 Endianness。

于 2009-02-06T11:35:49.703 回答
1

我强烈不同意逐字节阅读的想法。如果您在结构声明中处理结构包装,则可以毫无问题地复制到结构中。对于字节序问题,再次逐字节读取可以解决问题,但不会为您提供通用解决方案。这种方法很鸡肋。我以前为类似的工作做过类似的事情,它工作得很好,没有出现故障。

想想这个。我有一个结构,我也有那个结构的相应定义。您可以手动构建它,但我已经为此编写了一个解析器并将其用于其他事情。

例如,您上面给出的结构的定义是“sis s”。( s = short , i = int ) 然后我将结构地址、这个结构的这个定义和结构打包选项提供给一个处理字节序的特殊函数,瞧,它就完成了。

SwitchEndianToBig(&header, "sis s", 4); // 4 = 结构打包选项

于 2009-02-06T12:27:45.433 回答
1

如果我错了,请告诉我,但是 AFAIK,这样做可以保证您的数据是正确的 -假设类型在您的不同平台上具有相同的大小

#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) 以确保每种使用的类型的大小相同。

于 2009-02-06T18:07:32.143 回答
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;
}

摆脱了编译指示,但它不像我想要的那样通用。每次向标题添加字段时,都必须修改 << 函数。你能以某种方式迭代结构字段,获取字段的类型并调用相应的函数吗?

于 2009-02-06T12:16:30.853 回答