3

我正在做一个需要通过 TCP/IP 协议与设备通信的项目。该设备发送大量数据,我想以某种方式将其解析为一些对象/结构。

数据包示例(在 TCP 缓冲区 [] 中):

[64] [1] [78] [244] [77] [189] [249] [149] hcurrent
[64] [1] [78] [247] [89] [95] [104] [85] htarget
[0] [0] [0] [0] [0] [0] [0] [0] qcurrent
[188] [220] [97] [3] [66] [62] [0] [0] kcurrent
[66] [0] [102] [103] [66] [99][153] [154] mcurrent
[253] [191] [246] [74] [170] [216] [242] [29] fmode
[102] [191] [246] [74] [178] [44] [92] [72] tmil
[137] mode

现在这个包框架被标识为:

 double hcurrent
 double htarget
 double qcurrent
 float kcurrent
 float mcurrent
 float fmode
 float tmil
 unsigned char mode

我的想法是,我可以以某种方式将数据直接解析为具有与上述相同结构的结构。当然,需要识别一些关键值来确定它是哪种数据。

如何才能做到这一点?

由于我正在为 iOS 设备编写代码,因此它必须是 Objective-C 或 C(++)。

编辑(测试将数据报的每个部分复制到结构中的方法):小型 Java 实现,我尝试读取前 4 个字节[0] [0] [1] [5]

byte[] read = new byte[4]; 
int length = 0;
while (length < read.length) {
    len = iStream.read(read, len, read.length);
}
int ByteLength = (int)unsignedIntToLong(read);
ByteLength = ByteLength-5;
state = 1; // Continue and work with next data.

以及位操作方法:

public long unsignedIntToLong(byte[] b) 
{
    long l = 0;
    l |= b[0] & 0xFF;
    l <<= 8;
    l |= b[1] & 0xFF;
    l <<= 8;
    l |= b[2] & 0xFF;
    l <<= 8;
    l |= b[3] & 0xFF;
    return l;
}

所以我获取了前面提到的前 4 个字节,它决定了一些特定的东西,最后找到了465. 我的计划是对接收到的数据的所有其他部分重复此过程。

4

2 回答 2

4

您将遇到的最大问题是结构不会以完全连续的形式存储数据,它们会根据字边界对齐数据

这意味着你不能简单地定义一个结构然后将你的 buffer[] 转换为它,如果缓冲区不包含一个结构。相反,您可能需要做的是声明一个结构,然后使用指向缓冲区 [] 的指针偏移量一次将缓冲区 [] 的每个部分存储在一个字段中。

如果这种方法太麻烦,通常可以关闭结构对齐,以便结构可以表示完全打包的数据。MSVC 允许使用#pragma pack来执行此操作。然而,这种方法确实减慢了对结构的内存访问。

编辑:这是一个示例,它显示了如何使用模板函数从缓冲区读取任何类型,然后将偏移量更新到该缓冲区。您可以使用此方法安全地将任意数量的类型一一解析为结构:

// We want to copy raw data to this structure
// but the short will cause it to be unaligned
struct _parsed_structure
{
    int a;
    int b;
    short c;
    int d;
} parsed_structure;

template<typename T>
void read_and_update_offset (int & offset, char * buffer, T & var)
{
    T * pInt = (T*)(buffer + offset);
    var = *pInt;
    offset += sizeof(T);
};

int _tmain(int argc, _TCHAR* argv[])
{
    // Here's a buffer which we know contains ints and shorts, we could just cast it to our structure
    // but this will cause errors because the structure will not be aligned properly.
    char buffer[] = { 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0 };

    // Read the first int from the buffer into the structure
    int offset = 0;
    read_and_update_offset(offset, buffer, parsed_structure.a);
    read_and_update_offset(offset, buffer, parsed_structure.b);
    read_and_update_offset(offset, buffer, parsed_structure.c);
    read_and_update_offset(offset, buffer, parsed_structure.d);

    // Print the values
    std::cout << 
        parsed_structure.a << " " <<
        parsed_structure.b << " " <<
        parsed_structure.c << " " <<
        parsed_structure.d << " " << std::endl;

    // Look the size of our structure is different than the size of our buffer due to alignment
    std::cout <<
        "sizeof(buffer)" << "==" << sizeof(buffer) << " " <<
        "sizeof(parsed_structure)" << "==" << sizeof(parsed_structure) << std::endl;

    return 0;
}
于 2012-05-01T08:28:10.237 回答
2

处理此类事情的常用方法是编写方法nextInt,nextDouble等,这些方法将从流中读取字节(以正确的“endian”顺序)并返回指定类型的值,同时更新指向数组的指针或索引。这比尝试内联转换更易于管理,并且非常有效。(您可以使用 C++ 与 Objective-C 来提高效率。)

于 2012-05-01T12:49:21.573 回答