J'ai une petite application de serveur client dans laquelle je souhaite envoyer une structure entière sur une prise TCP en C ++. Supposons que la structure soit la suivante:
struct something{ int a; char b[64]; float c; }
8 Réponses :
Vous pouvez utiliser un De cette façon, vous pouvez envoyer et recevoir juste arr. Bien sûr, vous devez prendre soin des problèmes d'endianess et Union code> avec la structure que vous souhaitez envoyer et un tableau:
Tailleof (struct quelque chose) code> peut varier dans des machines (mais vous pouvez facilement surmonter cela avec un
#pragma pack code>). p> p>
Avant d'envoyer des données sur une connexion TCP, définissez une spécification de protocole. Il ne doit pas nécessairement être un document de plusieurs pages rempli de jargon technique. Mais il faut spécifier qui transmet quoi quand et il doit spécifier tous les messages au niveau des octets. Il devrait préciser comment les extrémités des messages sont établies, qu'il y ait des délais d'attente et qui les impose, etc. p>
Sans spécification, il est facile de poser des questions simplement impossibles à répondre. Si quelque chose ne va pas, quelle fin est en faute? Avec une spécification, la fin qui n'a pas suivi la spécification est en faute. (Et si les deux extrémités suivent la spécification et cela ne fonctionne toujours pas, la spécification est en défaut.) P>
Une fois que vous avez une spécification, il est beaucoup plus facile de répondre aux questions sur la manière dont une extrémité ou l'autre doit être conçue. P>
Je recommande également fortement pas em> concevoir un protocole réseau autour des spécificités de votre matériel. Au moins, pas sans problème de performance éprouvé. P>
Pourquoi feriez-vous cela quand il y a de bonnes et rapides bibliothèques de sérialisation, comme Message Pack qui fait tout le dur Travaillez pour vous et en bonus, ils vous fournissent une compatibilité entre langage de votre protocole de socket? P>
Utilisez le message de message ou une autre bibliothèque de sérialisation pour le faire. P>
Je ne suis pas autorisé à utiliser des bibliothèques externes. : /
Pragma Pack est utilisé pour la compatibilité binaire de votre structure sur une autre extrémité. Parce que le serveur ou le client sur lequel vous envoyez la structure peut être écrit sur une autre langue ou construite avec un autre compilateur C ou avec d'autres options de compilateur C. P>
sérialisation, si je comprends bien, fait du flux d'octets de votre structure. Lorsque vous vous écrivez, struct dans la prise, vous faites SerialIazation. P>
Cela dépend de savoir si vous pouvez être sûr que vos systèmes à l'une ou l'autre extrémité de la connexion sont homogènes ou non. Si vous êtes sûr, pour tout le temps (que la plupart d'entre nous ne peuvent pas être), vous pouvez prendre quelques raccourcis - mais vous devez savoir qu'ils sont des raccourcis.
struct something some; ... if ((nbytes = write(sockfd, &some, sizeof(some)) != sizeof(some)) ...short write or erroneous write...
Cela fonctionnerait dans certains cas, mais il y a une bonne probabilité que mon serveur et votre client soient des machines de 32 et 64 bits de sorte que la fonction Tailleof (STRIT) renvoie différentes valeurs de la taille de la taille, car la taille de l'int augmentera de 4 octets à 8 octets.
Habituellement, la sérialisation apporte plusieurs avantages par ex. Envoi des bits de la structure sur le fil (avec E.G. Très souvent, les routines de sérialisation sont générées. Il y a même 20 ans, RPCXDR existait déjà à cette fin et les primitives de sérialisation XDR sont toujours dans de nombreux Libc. p> fwrite code>). p>
char b [80]; code> au lieu de
char b [64]; code> li>
Vous avez besoin des éléments suivants pour envoyer des structures sur le réseau:
emballez la structure. Pour les compilateurs de GCC et compatibles, faites-le avec n'utilisez aucun membre autre que des entiers non signés de taille fixe, d'autres structures emballées satisfaisant ces exigences ou des tableaux de l'un des anciens. Les entiers signés sont également corrects, à moins que votre machine n'utilise pas une représentation complémentaire de deux deux. P> li>
Décidez si votre protocole utilisera un encodage peu ou grostigné des entiers. Faire des conversions lors de la lecture et de l'écriture de ces entiers. P> li>
aussi, Ne prenez pas les pointeurs de membres d'une structure emballée forte>, sauf à ceux avec la taille 1 ou d'autres structures emballées imbriquées. Voir Cette réponse . P> Li>
ul> Un exemple simple d'encodage et de décodage suit. Il suppose que les fonctions de conversion de commande d'octets en ce qui concerne les fonctions de conversion de la commande d'octet, aussi loin Comme les entiers signés sont concernés, les fonctions de conversion peuvent être mises en œuvre en toute sécurité de la même manière que celles que j'ai écrites et liées (échange d'octets directement); Il suffit de changer non signé en signé. p> update strong>: Si vous voulez un protocole binaire efficace, mais n'aime pas le violon avec les octets, vous pouvez essayer quelque chose comme __ attribut __ ((emballé)) code>. P> li>
hton8 () code>,
NTOH8 () code>,
hton32 () code> et
NTOH32 () code> sont disponibles (les deux premiers sont un non-op, mais là-bas pour la cohérence). p>
de votre système htons () code> /
NTOHS () code> et
htonl () code> /
NTOHL () code> peut être utilisé, pour 16 et 32- Des entiers de bits, respectivement, de convertir / de Big-Endian. Cependant, je ne suis au courant d'aucune fonction standard pour les entiers 64 bits ou de convertir / de la petite Endian. Vous pouvez utiliser My Byte Commandit Conversion Fonctions ; Si vous le faites, vous devez le dire l'ordre d'octet de votre machine em> en définissant
badvpn_little_endian code> ou
badvpn_big_endian code>. p>
Je vais essayer cette méthode, je veux juste demander si j'utilise simplement Sprintf et écrivez toutes les données en chaîne à l'aide d'un délimiteur pour séparer les éléments de la structure et envoyer sur la prise, puis utilisez Strtok pour extraire chaque élément sur le autre côté ? Serait-ce une solution réalisable aussi?
Oui, Sprintf fonctionnera, mais seulement i> pour les entiers; Si vous vouliez envoyer une chaîne (c'est-à-dire une gamme d'octets bruts), en utilisant cette méthode, vous devez les traiter comme une matrice d'octets et convertir chaque octet en un entier, insérant des espaces compris entre les deux. Par exemple, "ABC" serait envoyé comme "97 98 99". Cela pourrait être préférable car il est plus facile d'analyser lors du débogage, mais il est maladroit d'encoder / décoder, surtout si vous souhaitez vérifier l'erreur complète lors du décodage.
Quelle est la motivation derrière votre deuxième point de balle - en utilisant des entiers non signés. Pourquoi les caractères ne pouvaient-ils pas être utilisés dans les structs (ou les tableaux de caractère) pour envoyer des lettres, des octets ou des cordes?
Pourquoi le recv_data code> initialisé du type
uint8_t * code>, pourquoi pas
char * code>?
Le tampon de protocole Google propose une solution nifty à ce problème. Reportez-vous ici Buffer Google Protobol - C Implémentation
Créer un fichier .Proto basé sur Structure de votre charge utile et enregistrez-la comme compilez le fichier .poto à l'aide de p> Ceci créera le fichier d'en-tête Créer Votre fichier sur votre côté de réception client.c strong> < / p> Assurez-vous de compiler vos programmes avec