IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

C++ Socket envoi de structures sérialisées


Sujet :

C++

  1. #1
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Février 2011
    Messages
    3
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2011
    Messages : 3
    Points : 1
    Points
    1
    Par défaut C++ Socket envoi de structures sérialisées
    Bonjour a tous,

    Dans le cadre d'un projet scolaire je dois coder un serveur tcp qui est censé réceptionner des structures sur le réseau via socket TCP. Je ne suis pas autorisé a utiliser de librairies type Qt ou Boost pour la sérialisation ou pour le réseau.

    Mon problème est que les données que je reçoit sont corrompues. Excuser moi pour la longueure du code que je m'apprête a poster mais si vous voulez avoir une chance de m'aider il vous faut tout les éléments.

    La structure que j'envoie ( c'est Packet) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    struct Header
    {
      uint32_t      magic;
      uint32_t      checksum;
      uint32_t      timestamp;
      uint16_t      commandId;
      uint16_t      dataSize;
    };
     
    struct Packet
    {
      struct Header header;
      char          data[128];
    };
    Je sérialise cette structure avec ces fonctions
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
     
    unsigned char                           *Serialization::serialize_uint32(unsigned char *buffer, uint32_t arg)
    {
      buffer[3] = (arg >> 24);
      buffer[2] = (arg >> 16);
      buffer[1] = (arg >> 8);
      buffer[0] = (arg);
      return (buffer + sizeof(uint32_t));
    }
     
    unsigned char                           *Serialization::serialize_uint16(unsigned char *buffer, uint16_t arg)
    {
      buffer[1] = (arg >> 8);
      buffer[0] = (arg);
      return (buffer + sizeof(uint16_t));
    }
     
    unsigned char                           *Serialization::serialize_char(unsigned char *buffer, char arg)
    {
      buffer[0] = (arg);
      return (buffer + sizeof(char));
    }
     
    unsigned char                           *Serialization::serialize_string(unsigned char *buffer, char *arg, int len)
    {
      int                           i;
     
      i = -1;
      while (++i < len)
        buffer = serialize_char(buffer, arg[i]);
      return (buffer);
    }
     
    unsigned char                           *Serialization::serialize_header(unsigned char *buffer, struct Header& arg)
    {
      buffer = serialize_uint32(buffer, arg.magic);
      buffer = serialize_uint32(buffer, arg.checksum);
      buffer = serialize_uint32(buffer, arg.timestamp);
      buffer = serialize_uint16(buffer, arg.commandId);
      buffer = serialize_uint16(buffer, arg.dataSize);
      return (buffer);
    }
     
    unsigned char                           *Serialization::serialize_packet(unsigned char *buffer, struct Packet& arg)
    {
      buffer = serialize_header(buffer, arg.header);
      buffer = serialize_string(buffer, arg.data, arg.header.dataSize);
      return (buffer);
    }
    je désérialise ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
     
    unsigned char                           *Serialization::deserialize_uint32(unsigned char *buffer, uint32_t *arg)
    {
      memcpy((char*)arg, buffer, sizeof(uint32_t));
      *arg = ntohl(*arg);
      return (buffer + sizeof(uint32_t));
    }
     
    unsigned char                           *Serialization::deserialize_uint16(unsigned char *buffer, uint16_t *arg)
    {
      memcpy((char*)arg, buffer, sizeof(uint16_t));
      return (buffer + sizeof(uint16_t));
    }
     
    unsigned char                           *Serialization::deserialize_char(unsigned char *buffer, char *arg)
    {
      memcpy(arg, buffer, sizeof(char));
      return (buffer + sizeof(char));
    }
     
    unsigned char                           *Serialization::deserialize_string(unsigned char *buffer, char *arg, int len)
    {
      memcpy(arg, buffer, len);
      return (buffer + len);
    }
     
    unsigned char                           *Serialization::deserialize_header(unsigned char *buffer, struct Header& arg)
    {
      buffer = deserialize_uint32(buffer, &arg.magic);
      buffer = deserialize_uint32(buffer, &arg.checksum);
      buffer = deserialize_uint32(buffer, &arg.timestamp);
      buffer = deserialize_uint16(buffer, &arg.commandId);
      buffer = deserialize_uint16(buffer, &arg.dataSize);
      return (buffer);
    }
     
    unsigned char                           *Serialization::deserialize_packet(unsigned char *buffer, struct Packet& arg)
    {
      buffer = deserialize_header(buffer, arg.header);
      buffer = deserialize_string(buffer, arg.data, arg.header.dataSize);
      return (buffer);
    }
    Le code d'envoi de mon client est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    TcpSocket                     tcp;
    Packet                        p;
    char                          *serialized;
     
    tcp.connectSocket("127.0.0.1", 4242);
    p.header.magic = 0;
    p.header.checksum = 1;
    p.header.timestamp = 2;
    p.header.commandId = 3;
    p.header.dataSize = ss.str().length();
    memset(p.data, 0, 128);
    memcpy(p.data, "0", 2);
    serialized = new char[sizeof(Header) + 2];
    bzero(serialized, sizeof(Header) + 2);
    Serialization::serialize_packet(serialized, p);
    hexDump("serialized", serialized+1, sizeof(Header) + 2);
    ret = tcp.write(serialized, sizeof(Header) + 3);
    et mon serveur recoit ainsi (cette fonction est appellé par la boucle sur le select()) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int bav;
     
    bav = socket->bytesAvailable();
    buff = new char[bav];
    socket->read(buff, bav);
    hexdump("buff", buff, bav);
    methodes read et send de ma classe Socket :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
    int                     TcpSocket::read(char *buff, int len)
    {
      int                   ret;
     
      ret = recv(this->_socket, buff, len, 0);
      return (ret);
    }
     
    int                     TcpSocket::bytesAvailable()
    {
      int                   ret;
      char                  buff[65536];
     
      ret = recv(this->_socket, buff, 65535, MSG_PEEK);
      return (ret);
    }
     
    int                     TcpSocket::write(const std::string& buff, int len)
    {
      int                   ret;
     
      ret = send(this->_socket, buff.c_str(), len, 0);
      return (ret);
    }
    voici les deux hexdumps :

    client :
    00 00 00 00 00 00 01 00 00 00 02 00 03 00 01 30 ...............0

    server:
    00 00 00 00 14 00 00 00 1c 00 00 00 1a 00 00 00 ...............

    J'ai cherché pendant plusieurs jours avant de venir vous ennuyer avec mon problème.
    En vous remerciant par avance

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Quand tu déserialises, tu utilises memcopy directement, donc tu devrait en faire autant avec serialise.

    Ton problème, c'est que l'endianess est fixé dans ta serialisation (big endian) mais pas dans la deserialisation.
    Comme je parie que ta machine est little endian, tu te fais avoir.

    En général, quand on code deux opérations duales, il faut s'assurer que le code est bien symétrique.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Février 2011
    Messages
    3
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2011
    Messages : 3
    Points : 1
    Points
    1
    Par défaut
    Je suis d'accord avec toi sur un point. Je devrais désérialiser en décallage binaire mes données, mais je n'ai pas réussi a la faire.
    Avec memcpy, quand je sérialise et déserialise mon Packet dans un meme scope, je retrouve tout mes données intactes.
    Si je me faisais avoir par l'endianess mes données devrais être strictement inversées or dans le hexdump que je fournis 01 n'est pas la symétrie de 14

  4. #4
    Membre averti
    Homme Profil pro
    Java
    Inscrit en
    Mai 2011
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2011
    Messages : 170
    Points : 444
    Points
    444
    Par défaut
    Sinon ne pas oublier les middle-endian s'il-vous-plaît. Peut être pas répandu mais ça existe et qui sait ;-)

  5. #5
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Février 2011
    Messages
    3
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2011
    Messages : 3
    Points : 1
    Points
    1
    Par défaut
    J'effectue tous mes test en localhost, donc l'endianess (dans mon cas) n'est pas la source d'erreur

  6. #6
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par leternel Voir le message
    Quand tu déserialises, tu utilises memcopy directement, donc tu devrait en faire autant avec serialise.

    Ton problème, c'est que l'endianess est fixé dans ta serialisation (big endian) mais pas dans la deserialisation.
    Comme je parie que ta machine est little endian, tu te fais avoir.

    En général, quand on code deux opérations duales, il faut s'assurer que le code est bien symétrique.
    Dès lors que l'endianness s'en mèle, memcpy risque de faire des choses étranges Avec l'avènement de toutes les plateformes ARM (big endian par défaut) dans le monde du mobile, il est peut-être judicieux de faire les choses correctement. Et les choses correctes ne sont pas compliquées, vu qu'on nous fournit les fonction qui doivent être utilisées : htonl/htons et ntohl/ntohs (h = host, n = network : l = long (32 bits), s = short (16 bits)).

    Quand on envoie des entiers sur le réseau : htonl/s ; pour les récupérer du réseau, la fonction inverse ntohl/s. Ces fonctions garantissent que les données qui circulent sont dans un boutisme déterminé (boutisme réseau : big endian), et qu'on les transforme systèmatiquement pour un usage sur le host.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

Discussions similaires

  1. Envoi de structure via socket
    Par RoZyk dans le forum Réseau
    Réponses: 4
    Dernier message: 09/11/2010, 10h01
  2. windows socket, problème envoi de structure :s
    Par ramislebob dans le forum Réseau
    Réponses: 7
    Dernier message: 29/07/2006, 23h17
  3. [SOCKET] Envoi de structure
    Par Lolita59 dans le forum Réseau
    Réponses: 3
    Dernier message: 17/05/2006, 15h30
  4. [toFAQ][socket] Envoi d'une structure
    Par julien20vt dans le forum C++
    Réponses: 15
    Dernier message: 23/04/2003, 15h47
  5. [Socket]envoie de fichier!!!
    Par SamDaKap dans le forum C++Builder
    Réponses: 5
    Dernier message: 20/11/2002, 08h07

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo