Passando una struttura attraverso Sockets in C

Sto provando a passare l’intera struttura da client a server o viceversa. Assumiamo la mia struttura come segue

struct temp { int a; char b; } 

Sto usando sendto e inviando l’indirizzo della variabile di struttura e ricevendolo dall’altra parte usando la funzione recvfrom . Ma non sono in grado di ottenere i dati originali inviati sul lato ricevente. Nella funzione sendto sto salvando i dati ricevuti nella variabile di tipo struct temp.

 n = sendto(sock, &pkt, sizeof(struct temp), 0, &server, length); n = recvfrom(sock, &pkt, sizeof(struct temp), 0, (struct sockaddr *)&from,&fromlen); 

Dove pkt è la variabile di tipo struct temp.

Anche se sto ricevendo 8 byte di dati, ma se provo a stamparlo sta semplicemente mostrando i valori della spazzatura. Qualsiasi aiuto per una correzione su di esso?

NOTA: non è necessario utilizzare librerie di terze parti.

EDIT1: Sono davvero nuovo a questo concetto di serializzazione .. Ma senza fare serializzazione non posso inviare una struttura tramite prese?

EDIT2: Quando provo a inviare una stringa o una variabile intera usando le funzioni sendto e recvfrom , sto ricevendo i dati correttamente alla fine del ricevitore. Perché non nel caso di una struttura? Se non devo usare la funzione di serializzazione, allora dovrei inviare ogni singolo membro della struttura individualmente? Questa non è una soluzione adatta in quanto se ci sono “n” numero di membri, allora ci sono “n” numero di righe di codice aggiunte solo per inviare o ricevere dati.

Questa è una pessima idea. I dati binari dovrebbero sempre essere inviati in un modo che:

  • Gestisce endianness differenti
  • Gestisce imbottiture differenti
  • Gestisce le differenze nelle dimensioni in byte dei tipi intrinseci

Non scrivere mai un’intera struttura in un modo binario, non in un file, non in un socket.

Scrivi sempre ogni campo separatamente e leggili allo stesso modo.

Hai bisogno di avere funzioni come

 unsigned char * serialize_int(unsigned char *buffer, int value) { /* Write big-endian int value into buffer; assumes 32-bit int and 8-bit char. */ buffer[0] = value >> 24; buffer[1] = value >> 16; buffer[2] = value >> 8; buffer[3] = value; return buffer + 4; } unsigned char * serialize_char(unsigned char *buffer, char value) { buffer[0] = value; return buffer + 1; } unsigned char * serialize_temp(unsigned char *buffer, struct temp *value) { buffer = serialize_int(buffer, value->a); buffer = serialize_char(buffer, value->b); return buffer; } unsigned char * deserialize_int(unsigned char *buffer, int *value); 

O l’equivalente, ci sono naturalmente diversi modi per impostare questo per quanto riguarda la gestione del buffer e così via. Quindi è necessario eseguire le funzioni di livello superiore che serializzano / deserializzano intere strutture.

Ciò presuppone che la serializzazione sia eseguita su / da buffer, il che significa che la serializzazione non ha bisogno di sapere se la destinazione finale è un file o un socket. Significa anche che si paga un sovraccarico di memoria, ma in genere è un buon design per motivi di prestazioni (non si vuole scrivere una write () di ogni valore sul socket).

Una volta ottenuto quanto sopra, ecco come è ansible serializzare e trasmettere un’istanza di struttura:

 int send_temp(int socket, const struct sockaddr *dest, socklen_t dlen, const struct temp *temp) { unsigned char buffer[32], *ptr; ptr = serialize_temp(buffer, temp); return sendto(socket, buffer, ptr - buffer, 0, dest, dlen) == ptr - buffer; } 

Alcuni punti da notare su quanto sopra:

  • La struttura da inviare viene prima serializzata, campo dopo campo, nel buffer .
  • La routine di serializzazione restituisce un puntatore al successivo byte libero nel buffer, che usiamo per calcolare quanti byte è serializzato a
  • Ovviamente le mie routine di serializzazione di esempio non proteggono dall’overflow del buffer.
  • Il valore restituito è 1 se la chiamata sendto() riuscita, altrimenti sarà 0.

L’uso dell’opzione del pacchetto ‘pragma’ ha risolto il mio problema ma non sono sicuro che abbia delle dipendenze ??

 #pragma pack(1) // this helps to pack the struct to 5-bytes struct packet { int i; char j; }; #pragma pack(0) // turn packing off 

Quindi le seguenti righe di codice funzionavano bene senza alcun problema

 n = sendto(sock,&pkt,sizeof(struct packet),0,&server,length); n = recvfrom(sock, &pkt, sizeof(struct packet), 0, (struct sockaddr *)&from, &fromlen); 

Se non si desidera scrivere da soli il codice di serializzazione, trovare un framework di serializzazione appropriato e utilizzarlo.

Forse i buffer del protocollo di Google sarebbero possibili?

Non è necessario scrivere le proprie routine di serializzazione per i tipi interi short e long : utilizzare le funzioni POSIX htons() / htonl() .

La serializzazione è una buona idea. Puoi anche usare “wireshark” per monitorare il traffico e capire cosa viene effettivamente passato nei pacchetti.

Invece di serializzare e dipendere da librerie di terze parti è facile trovare un protocollo primitivo che usa tag, lunghezza e valore.

 Tag: 32 bit value identifying the field Length: 32 bit value specifying the length in bytes of the field Value: the field 

Concatena come richiesto. Usa le enumerazioni per i tag. E usa l’ordine dei byte di rete …

Facile da codificare, facile da decodificare.

Inoltre, se si utilizza TCP, ricordare che si tratta di un stream di dati, pertanto se si inviano, ad esempio, 3 pacchetti, non si riceveranno necessariamente 3 pacchetti. Potrebbero essere “uniti” in un stream in base all’algoritmo nodelay / nagel tra le altre cose e potresti averli tutti in una sola recv … Devi delimitare i dati per esempio usando RFC1006.

UDP è più semplice, riceverai un pacchetto distinto per ogni pacchetto inviato, ma è molto meno sicuro.

Se il formato dei dati che si desidera trasferire è molto semplice, la conversione da e verso una stringa ANSI è semplice e portatile.