Pregunta

Estoy enviando y recibiendo datos binarios hacia/desde un dispositivo en paquetes (64 bytes).Los datos tienen un formato específico, partes del cual varían según la diferente solicitud/respuesta.

Ahora estoy diseñando un intérprete para los datos recibidos.Simplemente leer los datos por posiciones está bien, pero no parece tan interesante cuando tengo una docena de formatos de respuesta diferentes.Actualmente estoy pensando en crear algunas estructuras para ese propósito, pero no sé cómo irá con el relleno.

¿Quizás haya una mejor manera?


Relacionado:

¿Fue útil?

Solución

He hecho esto innumerables veces antes:Es un escenario muy común.Hay una serie de cosas que prácticamente siempre hago.

No se preocupe demasiado por convertirlo en lo más eficiente disponible.

Si terminamos pasando mucho tiempo empacando y desempaquetando paquetes, siempre podemos cambiarlo para que sea más eficiente.Aunque todavía no he encontrado ningún caso en el que haya tenido que hacerlo, ¡no he implementado enrutadores de red!

Si bien el uso de estructuras/uniones es el enfoque más eficiente en términos de tiempo de ejecución, conlleva una serie de complicaciones:convencer a su compilador para que empaquete las estructuras/uniones para que coincidan con la estructura de octetos de los paquetes que necesita, trabaje para evitar problemas de alineación y endianidad, y una falta de seguridad, ya que hay poca o ninguna oportunidad de realizar comprobaciones de cordura en las compilaciones de depuración.

A menudo termino con una arquitectura que incluye los siguientes tipos de cosas:

  • Una clase base de paquete.Todos los campos de datos comunes son accesibles (pero no modificables).Si los datos no se almacenan en un formato empaquetado, existe una función virtual que producirá un paquete empaquetado.
  • Varias clases de presentación para tipos de paquetes específicos, derivadas del tipo de paquete común.Si usamos una función de empaquetado, entonces cada clase de presentación debe implementarla.
  • Todo lo que pueda inferirse del tipo específico de clase de presentación (es decir,una identificación de tipo de paquete de un campo de datos común), se trata como parte de la inicialización y, por lo demás, no es modificable.
  • Cada clase de presentación se puede construir a partir de un paquete desempaquetado o fallará si los datos del paquete no son válidos para ese tipo.Luego, esto se puede empaquetar en una fábrica para mayor comodidad.
  • Si no tenemos RTTI disponible, podemos obtener "RTTI del pobre" usando la identificación del paquete para determinar qué clase de presentación específica es realmente un objeto.

En todo esto, es posible (aunque solo sea para compilaciones de depuración) verificar que cada campo modificable se establezca en un valor sensato.Si bien puede parecer mucho trabajo, hace que sea muy difícil tener un paquete con formato no válido, el contenido de un paquete preempaquetado se puede verificar fácilmente a simple vista usando un depurador (ya que todo está en variables de formato nativas normales de la plataforma).

Si tenemos que implementar un esquema de almacenamiento más eficiente, eso también puede incluirse en esta abstracción con un pequeño costo de rendimiento adicional.

Otros consejos

Es necesario utilizar estructuras y o uniones. Tendrá que asegurarse de que sus datos están correctamente embalado en ambos lados de la conexión y es posible que desee traducir desde y hacia la red de orden de bytes en cada extremo si hay alguna posibilidad de que ambos lados de la conexión podría estar en funcionamiento con una diferente endianess.

A modo de ejemplo:

#pragma pack(push)  /* push current alignment to stack */
#pragma pack(1)     /* set alignment to 1 byte boundary */
typedef struct {
    unsigned int    packetID;  // identifies packet in one direction
    unsigned int    data_length;
    char            receipt_flag;  // indicates to ack packet or keep sending packet till acked
    char            data[]; // this is typically ascii string data w/ \n terminated fields but could also be binary
} tPacketBuffer ;
#pragma pack(pop)   /* restore original alignment from stack */

y luego, cuando la asignación:

packetBuffer.packetID = htonl(123456);

y luego cuando se recibe:

packetBuffer.packetID = ntohl(packetBuffer.packetID);

Estas son algunas discusiones de Endianness y alineación y estructura de embalaje

Si no empaqueta la estructura que va a terminar alineado con los límites de palabra y la disposición interna de la estructura y su tamaño será incorrecto.

Es difícil decir cuál es la mejor solución es sin conocer el formato exacto (s) de los datos. Ha considerado el uso sindicatos?

Estoy de acuerdo con Wuggy. También puede utilizar la generación de código para hacer esto. Utilizar un simple archivo de definición de datos para definir todos los tipos de paquetes, a continuación, ejecute un script en Python sobre ella para generar estructuras de prototipos y / serialiation funciones unserialization para cada uno.

Esta es una solución "out-of-the-box", pero me gustaría sugerir a echar un vistazo a la Python construir biblioteca.

  

Construct es una biblioteca de Python para   el análisis y la construcción de los datos   estructuras (binarios o de texto). Está   basado en el concepto de datos que definen   estructuras de una manera declarativa,   en lugar de código de procedimiento: más   constructos complejos se componen de una   jerarquía de los más simples. Es el   primera biblioteca que se burla de análisis,   en lugar de la habitual es el dolor de cabeza   hoy en día.

construcción es muy robusta y de gran alcance, y sólo leer el tutorial le ayudará a comprender mejor el problema. El autor también tiene planes para el código de generación automática de las definiciones C, por lo que definitivamente vale la pena el esfuerzo de leer.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top