2009-02-06 10 views
5

Obtengo una transmisión a través de la red que es una matriz de caracteres/bytes. Contiene un encabezado y algunos datos. Me gustaría asignar el encabezado a una estructura. Aquí hay un ejemplo:Deserializar una matriz de bytes a una estructura

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

¿Funcionará esto siempre suponiendo que la estructura nunca contenga ningún campo de longitud variable? ¿Hay una plataforma independiente/idiomática para hacer esto?

Nota:

he visto bastantes bibliotecas en Internet que le permiten serializar/deserializar, pero me da la impresión de que sólo pueden deserializar algo si ha ben previamente serializado con la misma biblioteca . Bueno, no tengo control sobre el formato de la transmisión. Definitivamente voy a obtener una matriz byte/char donde todos los valores se siguen unos a otros.

Respuesta

5

Algunos procesadores requieren que ciertos tipos estén alineados correctamente. No aceptarán el embalaje especificado y generarán una trampa de hardware.

E incluso en las estructuras empaquetadas x86 comunes puede hacer que el código se ejecute más lentamente.

También deberá tener cuidado al trabajar con diferentes plataformas endianness.

Por cierto, si quiere un mecanismo de comunicación simple e independiente de la plataforma con enlaces a muchos lenguajes de programación, entonces eche un vistazo a YAMI.

5

Es muy probable que se rompa el copiado simple, al menos si los datos pueden provenir de una arquitectura diferente (o incluso solo del compilador) de lo que se encuentra. Esto es por razones de:

Ese segundo enlace es-CCG específico, pero esto se aplica a todos los compiladores.

Recomiendo leer los campos byte por byte y ensamblar campos más grandes (entradas, etc.) desde esos bytes. Esto le da control de endianness y relleno.

2

La directiva #pragma pack(1) debería funcionar en la mayoría de los compiladores, pero puede verificar el tamaño de su estructura de datos (10 en su caso si mis cálculos son correctos) y usar printf("%d", sizeof(Header)); para verificar que se realice el embalaje.

Como han dicho otros, aún debe tener cuidado con Endianness si va entre arquitecturas.

0

Sé con quién me estoy comunicando, así que realmente no tengo que preocuparme por la permanencia. Pero me gusta mantenerme alejado de los comandos específicos del compilador de todos modos.

Así que tal esto:

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

se deshace de la pragma, pero no es tan general como propósito que me gustaría. Cada vez que agrega un campo al encabezado, debe modificar la función < <. ¿Puedes iterar sobre los campos struct de alguna manera, obtener el tipo de campo y llamar a la función correspondiente?

+0

Acerca de iterar sobre campos struct: ¿Tiene que usar una estructura? Estoy preguntando porque reemplazarlo por una tupla permitiría la iteración de los campos. –

1

Estoy totalmente en desacuerdo con la idea de leer byte por byte. Si se ocupa de la estructura empaquetada en la declaración struct, puede copiar en la estructura sin ningún problema. Para el problema de Endiannes, leer nuevamente byte por byte resuelve el problema pero no proporciona una solución genérica. Ese método es muy cojo. He hecho algo así antes para un trabajo similar y funcionó bien sin ningún problema.

Piense en esto. Tengo una estructura, también tengo una definición correspondiente de esa estructura. Puedes construir esto a mano, pero he escrito un analizador para esto y lo usé para otras cosas también.

Por ejemplo, la definición de la estructura que proporcionó anteriormente es "s s s". (s = short, i = int) Luego le doy la dirección struct, esta definición y la opción de empaque de estructura de esta estructura a una función especial que trata con lo de endiannes y listo.

SwitchEndianToBig (& encabezado, "s s s", 4); // 4 = estructura de opciones de embalaje

1

Dime si estoy equivocado, pero que yo sepa, hacerlo de esa manera le garantiza que los datos son correctos - suponiendo que los tipos tienen el mismo tamaño en sus diferentes plataformas:

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

Si los tipos son diferentes en sus planchas específicas, debe usar alias (typedef) para cada tipo para asegurarse de que el tamaño de cada tipo utilizado sea el mismo.

+0

Uhm, vas al revés (convirtiendo una estructura en una matriz de bytes). E incluso eso no funciona como debería porque estás copiando el relleno de la estructura en la matriz. – drby

Cuestiones relacionadas