12
votes

passer une structure sur la prise TCP (sock_stream) en C

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


0 commentaires

8 Réponses :


1
votes

Vous pouvez utiliser un Union avec la structure que vous souhaitez envoyer et un tableau: xxx

De cette façon, vous pouvez envoyer et recevoir juste arr. Bien sûr, vous devez prendre soin des problèmes d'endianess et Tailleof (struct quelque chose) peut varier dans des machines (mais vous pouvez facilement surmonter cela avec un #pragma pack ).


0 commentaires

3
votes

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.

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

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.

Je recommande également fortement pas concevoir un protocole réseau autour des spécificités de votre matériel. Au moins, pas sans problème de performance éprouvé.


0 commentaires

1
votes

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?

Utilisez le message de message ou une autre bibliothèque de sérialisation pour le faire.


1 commentaires

Je ne suis pas autorisé à utiliser des bibliothèques externes. : /



0
votes

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.

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.


0 commentaires

2
votes

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...


1 commentaires

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.



1
votes

Habituellement, la sérialisation apporte plusieurs avantages par ex. Envoi des bits de la structure sur le fil (avec E.G. fwrite ).

  1. Cela se produit individuellement pour chaque donnée atomique non agrégée (par exemple. int).
  2. Il définit précisément le format de données série envoyé sur le fil
  3. Il s'agit de l'architecture hétérogène: l'envoi et la rétention de machines pourraient avoir une longueur de mot et une endansion différente.
  4. Il peut être moins fragile lorsque le type change un peu. Donc, si une machine a une ancienne version de votre code en cours d'exécution, elle pourrait peut-être parler avec une machine avec une version plus récente, par ex. un ayant un char b [80]; au lieu de char b [64];
  5. Cela peut traiter avec des structures de données plus complexes - des vecteurs de taille préciable, voire des tables hachées - avec une manière logique (pour la table de hachage, transmettent l'association, ..)

    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.


0 commentaires

18
votes

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 __ attribut __ ((emballé)) .

  • 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.

  • 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.

  • aussi, Ne prenez pas les pointeurs de membres d'une structure emballée , sauf à ceux avec la taille 1 ou d'autres structures emballées imbriquées. Voir Cette réponse .

    Un exemple simple d'encodage et de décodage suit. Il suppose que les fonctions de conversion de commande d'octets hton8 () , NTOH8 () , hton32 () et NTOH32 () sont disponibles (les deux premiers sont un non-op, mais là-bas pour la cohérence). xxx

    en ce qui concerne les fonctions de conversion de la commande d'octet, de votre système htons () / NTOHS () et htonl () / NTOHL () 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 en définissant badvpn_little_endian ou badvpn_big_endian .

    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é.

    update : Si vous voulez un protocole binaire efficace, mais n'aime pas le violon avec les octets, vous pouvez essayer quelque chose comme tampons de protocole ( C Mise en œuvre ). Cela vous permet de décrire le format de vos messages dans des fichiers distincts et génère un code source que vous utilisez pour encoder et décoder des messages du format que vous spécifiez. J'ai également mis en œuvre quelque chose de similaire moi-même, mais grandement simplifié; Voir Mon générateur BProto et Quelques exemples (regarde dans les fichiers .bproto et addr.h pour l'utilisation exemple).


4 commentaires

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 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 initialisé du type uint8_t * , pourquoi pas char * ?



0
votes

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 up utile.proto xxx

compilez le fichier .poto à l'aide de xxx

Ceci créera le fichier d'en-tête up utile.pb-ch et son PAYLOAD.PB-CC dans votre répertoire.

Créer Votre fichier Server.c Fichier et incluez les fichiers d'en-tête Protobuf-C xxx

sur votre côté de réception client.c < / p> xxx

Assurez-vous de compiler vos programmes avec -LProTobuf-C drapeau xxx


0 commentaires