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(++) でなければなりません。

EDIT(データグラムの各部分を構造体にコピーするためにテストされた方法):最初の4バイトを読み取ろうとする小さなJava実装[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[]をキャストすることはできません。代わりに、おそらく行う必要があるのは、構造体を宣言してから、buffer []へのポインタオフセットを使用して、一度に1つのフィールドでbuffer[]の各部分をmemcpyすることです。

このアプローチが面倒な場合は、構造アラインメントをオフにして、構造が完全にパックされたデータを表すことができるようにすることができます。MSVCでは、#pragmapackを使用てこれを行うことができます。ただし、このアプローチでは、構造体へのメモリアクセスが遅くなります。

編集:これは、テンプレート関数を使用してバッファーから任意のタイプを読み取り、そのバッファーへのオフセットを更新する方法を示す例です。このメソッドを使用して、任意の数のタイプを1つずつ構造に安全に解析できます。

// 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

このようなものへの通常のアプローチは、ストリームからバイトを (適切な「エンディアン」順で) 読み取り、指定された型の値を返し、ポインターまたはインデックスを配列に更新するメソッドを記述することですnextIntnextDoubleこれは、変換をインライン化するよりもはるかに扱いやすく、非常に効率的です。(効率のために C++ と Objective-C のメソッドを作成できます。)

于 2012-05-01T12:49:21.573 に答える