2010-11-18 4 views
5

J'écris un jeu multiplateforme avec des capacités de réseautage (en utilisant SFML et RakNet) et j'en suis arrivé au point où j'ai compilé le serveur sur mon serveur Ubuntu et ai obtenu un client sur mon Mac. Tout le développement est fait sur mon Mac donc j'ai d'abord testé le serveur là-dessus, et ça a bien fonctionné.Création et utilisation d'une structure multiplateforme en C++

J'envoie struct s sur le réseau, puis je les renvoie simplement de char * à (par exemple) inet::PlayerAdded. Maintenant, cela fonctionne très bien (pour la plupart), mais ma question est: Est-ce que cela fonctionnera toujours? Cela semble être une approche très fragile. La structure sera-t-elle toujours la même, même sur d'autres plateformes, Windows par exemple? Que recommanderais-tu?

#pragma pack(push, 1) 
struct Player 
{ 
    int dir[2]; 
    int left; 
    float depth; 
    float elevation; 
    float velocity[2]; 
    char character[50]; 
    char username[50]; 
}; 

// I have been added to the game and my ID is back 
struct PlayerAdded: Packet 
{ 
    id_type id; 
    Player player; 
}; 
#pragma pack(pop) 
+2

Pour le downvoter série, une raison particulière? (La downvote de Steve semble particulièrement flagrante) – KevinDTimm

+0

Les points n'ont aucun sens dans un endroit comme celui-ci. Je ne m'inquiéterais pas pour ça. –

+1

Je ne me soucie pas des points, je veux savoir pourquoi chaque réponse (sauf une, la moins utile de toutes) a obtenu un downvote. Surtout quand l'une des réponses (downvoted) était clairement la meilleure. – KevinDTimm

Répondre

5

Comme beaucoup d'autres réponses, je déconseille d'envoyer des données binaires brutes si cela peut être évité. Quelque chose comme Boost série ou Google Protobuf fera un bon travail sans trop de frais généraux.

Mais vous pouvez certainement créer des structures binaires multiplateformes, c'est fait tout le temps et c'est un moyen très valable d'échanger des données. Superposer un "struct" sur ces données a juste du sens. Vous devez cependant faire très attention à la mise en page, heureusement que la plupart des compilateurs vous donnent beaucoup d'options pour cela. "pack" est une telle option et prend soin de beaucoup.

Vous devez également faire attention aux tailles de données. Simple incluent stdint.h et utilisent les types de taille fixe comme uint32_t. Méfiez-vous des valeurs à virgule flottante, car toutes les architectures ne partagent pas la même valeur, pour un flottant de 32 bits. Aussi pour les endianess la plupart des architectures utiliseront la même chose, et si elles ne le font pas, vous pouvez simplement le retourner sur le client qui est différent.

+0

Même si la plupart des réponses étaient vraiment géniales, je pense que c'est celle qui m'a le plus aidé. Je pense que je vais tenter le coup, et moi si jamais je heurte une barrière avec cette méthode, je saurai quoi utiliser. – ErikPerik

9

Cela ne fonctionnera pas si (entre autres) que vous essayez de le faire de la machine little-endian à la machine big-endian comme la représentation int correcte sera inversée entre les deux.

Cela peut également échouer si l'alignement ou le remplissage de votre structure passe d'une machine à l'autre. Que faire si vous avez des machines 64 bits et d'autres 32 bits?

Vous devez utiliser une bibliothèque de sérialisation portable appropriée telle que Boost.Serialization ou Google Protocol Buffers pour vous assurer que vous disposez d'un protocole de câble (également connu sous le nom de format de données transmissible) décodable avec succès indépendamment du matériel. Une bonne chose à propos des tampons de protocole est que vous pouvez compresser les données de manière transparente en utilisant un flux compatible avec ZLIB qui est également compatible avec les flux protobuf. J'ai effectivement fait cela, ça marche bien. J'imagine que d'autres flux décoratifs peuvent être utilisés de manière analogue pour améliorer ou optimiser votre protocole de base de fil si nécessaire.

2

La réponse à "... disposé même sur d'autres plates-formes ..." est généralement non. Cela est vrai même si des problèmes tels que des processeurs différents et/ou des endianness différents sont adressés.

Différents systèmes d'exploitation (même sur la même plate-forme matérielle) peuvent utiliser différentes représentations de données; cela s'appelle normalement la "plate-forme ABI" et c'est différent entre par ex. Windows 32 bits/64 bits, Linux 32 bits/64 bits, MacOSX. '#pragma pack' est seulement la moitié du chemin, car au-delà des restrictions d'alignement, il peut y avoir des différences de taille de type de données. Par exemple, "long" sur Windows 64 bits est 32 bits alors qu'il est 64 bits sur Linux et MacOSX. Cela dit, le problème n'est évidemment pas nouveau et a déjà été abordé par le passé - le protocole d'appel de procédure distante (RPC) contient des mécanismes permettant de définir les structures de données indépendamment de la plate-forme et de coder/décoder les "tampons" représentant ces structures. C'est ce qu'on appelle "XDR" (eXternal Data Representation). Voir RFC1832. En cours de programmation, cette roue a été réinventée plusieurs fois; que vous convertissiez en XML, faites le bas niveau avec XDR, utilisez google :: protobuf, boost ou Qt :: Variant, il y a beaucoup à choisir. Du simple point de vue de la mise en œuvre: Pour simplifier, supposons que "entier non signé" est partout aligné à 32 bits à une limite de 32 bits; Si vous pouvez encoder toutes vos données sous la forme d'un tableau de valeurs 32 bits, alors le seul problème d'externalisation que vous avez à traiter est l'endianness.

Questions connexes