2009-05-12 14 views
5

Invio e ricevo dati binari da/a un dispositivo in pacchetti (64 byte). I dati hanno un formato specifico, le cui parti variano a seconda della richiesta/risposta.Come interpretare i dati binari in C++?

Ora sto progettando un interprete per i dati ricevuti. La semplice lettura dei dati per posizione è OK, ma non sembra così interessante quando ho una dozzina di formati di risposta diversi. Attualmente sto pensando di creare alcune strutture per questo scopo, ma non so come andrà con il padding.

Forse c'è un modo migliore?


correlati:

+0

Ero sicuro di aver visto un quasi duplicato di questo, ma non riuscivo a scavare. Le risposte nel link correlato non sono buone come quelle qui, ma comunque ... – dmckee

risposta

3

Ho già fatto innumerevoli volte: è uno scenario molto comune. C'è un numero di cose che faccio praticamente sempre.

Non preoccuparti troppo di renderlo la cosa più efficiente disponibile.

Se finiamo di perdere un sacco di tempo per impacchettare e svuotare i pacchetti, possiamo sempre cambiarlo per renderlo più efficiente. Anche se non ho ancora riscontrato un caso in cui ho dovuto, non ho implementato router di rete!

Anche se l'utilizzo di structs/unions è l'approccio più efficiente in termini di tempo di esecuzione, viene fornito con una serie di complicazioni: convincere il compilatore a comprimere le strutture/i sindacati in modo che corrispondano alla struttura di ottetti dei pacchetti necessari, evitare problemi di allineamento e endianità e mancanza di sicurezza poiché non vi è alcuna o minima opportunità di effettuare controlli di integrità sui build di debug.

spesso finiscono con un'architettura che include i seguenti tipi di cose: classe base

  • Un pacchetto. Tutti i campi dati comuni sono accessibili (ma non modificabili).Se i dati non sono memorizzati in un formato compresso, allora c'è una funzione virtuale che produrrà un pacchetto impacchettato.
  • Un numero di classi di presentazione per tipi di pacchetti specifici, derivati ​​dal tipo di pacchetto comune. Se stiamo usando una funzione di impacchettamento, allora ogni classe di presentazione deve implementarla.
  • Tutto ciò che può essere dedotto dal tipo specifico della classe di presentazione (ad esempio un ID di tipo di pacchetto da un campo dati comune), viene gestito come parte dell'inizializzazione ed è altrimenti non modificabile.
  • Ogni classe di presentazione può essere costruita da un pacchetto decompresso, oppure fallisce correttamente se i dati del pacchetto non sono validi per quel tipo. Questo può quindi essere confezionato in una fabbrica per comodità.
  • Se non è disponibile RTTI, è possibile ottenere "RTTI di poor-man" utilizzando l'id del pacchetto per determinare quale specifica classe di presentazione è realmente un oggetto.

In tutto questo, è possibile (anche se solo per le build di debug) verificare che ogni campo che è modificabile sia impostato su un valore corretto. Anche se potrebbe sembrare molto lavoro, rende molto difficile avere un pacchetto formattato in modo non valido, un contenuto di pacchetti preconfezionati può essere facilmente controllato a occhio utilizzando un debugger (poiché è tutto in normali variabili di formato nativo della piattaforma).

Se dobbiamo implementare uno schema di archiviazione più efficiente, anche questo può essere racchiuso in questa astrazione con costi di prestazioni aggiuntivi minimi.

+0

Questo tipo di progettazione funziona molto bene per i flussi di dati codificati in ASN.1. Ho appena implementato qualcosa del genere per analizzare LDAP – Matt

3

E 'difficile dire quale sia la soluzione migliore è senza conoscere il formato esatto (s) dei dati. Hai preso in considerazione l'utilizzo dei sindacati?

+0

Grazie! Un'ottima idea, anche se avrei comunque bisogno di una struttura o anche di un paio di loro. –

+3

Penso che implicasse le strutture e i sindacati usati insieme. Ma potrebbero ancora esserci problemi relativi ai buchi della struttura. –

8

È necessario utilizzare le strutture e/o le unioni. Avrai bisogno di assicurarti che i tuoi dati siano correttamente imballati su entrambi i lati della connessione e potresti voler eseguire la conversione da e verso l'ordine dei byte di rete ad ogni estremità se esiste la possibilità che entrambi i lati della connessione possano essere eseguiti con un altro endianess.

Per fare un esempio:

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

e poi quando si assegna:

packetBuffer.packetID = htonl(123456); 

e poi quando si riceve:

packetBuffer.packetID = ntohl(packetBuffer.packetID); 

Ecco alcune discussioni di Endianness e Alignment and Structure Packing

Se non si impacchetta la struttura, questa verrà allineata ai limiti delle parole e al layout interno della struttura e la sua dimensione sarà errata.

+4

Si noti che su alcuni processori come ARM si può ottenere un'eccezione di allineamento dei dati se si è tentato di accedere a data_length nell'esempio. – Steven

+0

@Steven buon punto. –

+0

Per risolvere il problema su cui Steven ha parlato dell'allineamento dei dati, è necessario aggiungere char pad [3]; dopo il ricevimento_flag. – zooropa

1

Sono d'accordo con Wuggy. Puoi anche usare la generazione del codice per farlo. Utilizzare un semplice file di definizione dei dati per definire tutti i tipi di pacchetti, quindi eseguire uno script Python su di esso per generare strutture prototipo e funzioni serialiation/unserialization per ognuno.

1

Questa è una soluzione "pronta all'uso", ma suggerisco di dare un'occhiata alla libreria Python construct.

Construct è una libreria Python per analisi e la costruzione dei dati strutture (binario o testuale). È basato sul concetto di definire le strutture in modo dichiarativo, piuttosto che codice procedurale: più I costrutti complessi sono composti da una gerarchia più semplice. È la prima libreria che rende divertente l'analisi, invece del solito mal di testa è oggi.

costrutto è molto robusto e potente, e basta leggere il tutorial per capire meglio il problema. L'autore ha anche piani per auto-generare il codice C dalle definizioni, quindi vale sicuramente la pena di leggere.