web-dev-qa-db-fra.com

Passer une structure via des sockets en C

J'essaye de passer la structure entière du client au serveur ou vice-versa. Supposons ma structure comme suit

struct temp {
  int a;
  char b;
}

J'utilise sendto et j'envoie l'adresse de la variable de structure et je la reçois de l'autre côté en utilisant la fonction recvfrom. Mais je ne suis pas en mesure d'obtenir les données d'origine envoyées du côté de la réception. Dans la fonction sendto, je sauvegarde les données reçues dans une variable de type struct temp.

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

Où pkt est la variable de type struct temp.

Même si je reçois 8 octets de données, mais si j'essaie de l'imprimer, cela montre simplement les valeurs de déchets. Avez-vous besoin d'aide pour y remédier?

REMARQUE:Aucune bibliothèque tierce partie ne doit être utilisée.

EDIT1: Je suis vraiment nouveau dans ce concept de sérialisation .. Mais sans faire de sérialisation ne puis-je pas envoyer une structure via des sockets?

EDIT2: Lorsque j'essaie d'envoyer une chaîne ou une variable entière en utilisant le - sendto et recvfrom fonctions Je reçois correctement les données côté récepteur. Pourquoi pas dans le cas d'une structure? Si je n'ai pas à utiliser la fonction de sérialisation, dois-je envoyer chaque membre de la structure individuellement? Ce n'est vraiment pas une solution appropriée car s'il y a 'n' nombre de membres alors il y a 'n' nombre de lignes de code ajoutées juste pour envoyer ou recevoir des données.

47
codingfreak

C'est une très mauvaise idée. Les données binaires doivent toujours être envoyées de manière à:

N'écrivez jamais une structure entière de manière binaire, ni dans un fichier, ni dans un socket.

Écrivez toujours chaque champ séparément et lisez-les de la même manière.

Vous devez avoir des fonctions comme

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

Ou l'équivalent, il y a bien sûr plusieurs façons de configurer cela en ce qui concerne la gestion des tampons, etc. Ensuite, vous devez effectuer les fonctions de niveau supérieur qui sérialisent/désérialisent des structures entières.

Cela suppose que la sérialisation est effectuée vers/depuis les tampons, ce qui signifie que la sérialisation n'a pas besoin de savoir si la destination finale est un fichier ou un socket. Cela signifie également que vous payez une surcharge de mémoire, mais c'est généralement une bonne conception pour des raisons de performances (vous ne voulez pas faire une écriture () de chaque valeur sur le socket).

Une fois que vous avez ce qui précède, voici comment vous pouvez sérialiser et transmettre une instance de structure:

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

Quelques points à noter sur ce qui précède:

  • La structure à envoyer est d'abord sérialisée, champ par champ, en buffer.
  • La routine de sérialisation renvoie un pointeur sur l'octet libre suivant dans le tampon, que nous utilisons pour calculer le nombre d'octets dans lesquels il est sérialisé.
  • Évidemment, mes exemples de routines de sérialisation ne protègent pas contre le débordement de la mémoire tampon.
  • La valeur de retour est 1 si l'appel sendto() a réussi, sinon il sera 0.
69
unwind

L'utilisation de l'option pack 'pragma' a résolu mon problème mais je ne sais pas s'il a des dépendances ??

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

Ensuite, les lignes de code suivantes ont bien fonctionné sans aucun problème

n = sendto(sock,&pkt,sizeof(struct packet),0,&server,length);

n = recvfrom(sock, &pkt, sizeof(struct packet), 0, (struct sockaddr *)&from, &fromlen);
10
codingfreak

Il n'est pas nécessaire d'écrire ses propres routines de sérialisation pour les types entiers short et long - utilisez htons()/htonl() fonctions POSIX.

7
qrdl

Si vous ne voulez pas écrire le code de sérialisation vous-même, trouvez un framework de sérialisation approprié et utilisez-le.

Peut-être que Google tampons de protocole serait possible?

6
Douglas Leeder

La sérialisation est une bonne idée. Vous pouvez également utiliser Wireshark pour surveiller le trafic et comprendre ce qui est réellement passé dans les paquets.

1
eyalm

Au lieu de sérialiser et en fonction des bibliothèques tierces, il est facile de trouver un protocole primitif utilisant la balise, la longueur et la valeur.

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

Concaténer au besoin. Utilisez des énumérations pour les balises. Et utilisez l'ordre des octets du réseau ...

Facile à encoder, facile à décoder.

De plus, si vous utilisez TCP rappelez-vous qu'il s'agit d'un flux de données, donc si vous envoyez par exemple 3 paquets, vous ne recevrez pas nécessairement 3 paquets. Ils peuvent être "fusionnés" dans un flux en fonction de l'algorithme nodelay/nagel entre autres et vous pouvez les obtenir tous en un seul recv ... Vous devez délimiter les données par exemple en utilisant RFC1006.

UDP est plus facile, vous recevrez un paquet distinct pour chaque paquet envoyé, mais c'est beaucoup moins sécurisé.

0
hplbsh

Si le format des données que vous souhaitez transférer est très simple, la conversion vers et depuis une chaîne ANSI est simple et portable.

0
the_mandrill