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 :

[Réseau] Envoi et réception de données


Sujet :

C++

  1. #1
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut [Réseau] Envoi et réception de données
    Bonjour à tous,

    Je me pose quelques questions quant au choix technique pour l'envoi et la réception de donnée client/serveur sur un réseau Internet, que ce soit en synchrone ou asynchrone.
    Dans le tuto de khayyam, le choix est porté sur une sérialisation et un envoi "brute" de données sur le réseau.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void network_machine::TCP_send(engine_event& e){
    	std::ostringstream archive_stream;
    	boost::archive::text_oarchive archive(archive_stream);
    	archive << e;
    	const std::string &outbound_data = archive_stream.str();
     
    	s_tcp->send(asio::buffer(outbound_data));
    }
    Avec la fonction receive associée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    void network_machine::TCP_async_receive(const asio::error_code& e, size_t bytes_received){
    	// let's deserialize the message
    	std::string str_data(&network_buffer[0], network_buffer.size());
    	std::istringstream archive_stream(str_data);
    	boost::archive::text_iarchive archive(archive_stream);
     
    	engine_event ne;
    	archive >> ne;
     
    	// add the event to the received event queue
    	parent->push_received_event(ne);
    }
    Celà pose plusieurs problèmes pour moi. Quelle doit être la taille du buffer pour la réception? Est-on sur de récupérer toutes les données?

    Dans un autre style, l'exemple du chat de boost choisit d'avoir un header avec la taille du message à envoyer...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    class chat_message
    {
    public:
      enum { header_length = 4 };
      enum { max_body_length = 512 };
       //....
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // Réception : lecture du header
    boost::asio::async_read(socket_,
            boost::asio::buffer(read_msg_.data(), chat_message::header_length),
            boost::bind(
              &chat_session::handle_read_header, shared_from_this(),
              boost::asio::placeholders::error));
    // puis réception du reste...
    // .........
    Là il faut avouer que c'est nickel. Par contre, tout est fixé à l'avance. Ca laisse peu de place à des messages sérialisés beaucoup plus compliqués possédant des std::map, std::string, std::vector et autres conteneurs dont on ne peut prédire la taille... (on peut tout de même calculer la taille des données, mais contrairement à l'exemple de boost, tout ne serait pas réglé à la compilation)

    En conclusion, faut-il systématiquement envoyer la taille des données que l'on va transmettre suivant un protocole bien établi entre le client et le serveur, ou bien l'envoi plus "freestyle" convient aussi bien dans certains cas?

    Merci pour vos éclaircissements...

  2. #2
    Rédacteur

    Avatar de ram-0000
    Homme Profil pro
    Consultant en sécurité
    Inscrit en
    Mai 2007
    Messages
    11 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Consultant en sécurité
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2007
    Messages : 11 517
    Points : 50 367
    Points
    50 367
    Par défaut
    Personnellement, je suis assez partisan du concept <longueur><valeur>

    Cela permet d'abord d'allouer un buffer de réception.
    Cela permet aussi d'assurer la synchronisation emission/réception. En effet, 3 émissions TCP peuvent produire 2 réceptions et l'inverse aussi, c'est à dire que 2 émissions peuvent produire 3 réceptions.
    Raymond
    Vous souhaitez participer à la rubrique Réseaux ? Contactez-moi

    Cafuro Cafuro est un outil SNMP dont le but est d'aider les administrateurs système et réseau à configurer leurs équipements SNMP réseau.
    e-verbe Un logiciel de conjugaison des verbes de la langue française.

    Ma page personnelle sur DVP
    .

  3. #3
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    L'autre solution, sinon, c'est d'avoir un terminateur sur tes messages. Ça impose par contre d'échapper un minimum les données que tu envoies.

  4. #4
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Citation Envoyé par ram-0000 Voir le message
    Cela permet aussi d'assurer la synchronisation emission/réception. En effet, 3 émissions TCP peuvent produire 2 réceptions et l'inverse aussi, c'est à dire que 2 émissions peuvent produire 3 réceptions.
    C'est clair, c'est vraiment le principal problème je trouve. On est partisan du même type de fonctionnement. Ce qui m'a fait poser cette question, c'est que Khayyam dans son tuto récupère de manière brute des données sur le réseau et désérialise à la volée. Comment être sûr qu'on a pas récupèrer des octets en plus ou moins? La désérialisation échoue dans ce cas là non? J'ai un gros doute sur le bon fonctionnement de cette architecture
    Citation Envoyé par white_tentacle
    L'autre solution, sinon, c'est d'avoir un terminateur sur tes messages. Ça impose par contre d'échapper un minimum les données que tu envoies.
    C'est effectivement une solution que j'avais utilisé il y a longtemps. Ca me semble moins adapté en général, mais oui j'avais eu de bons résultats!

  5. #5
    Membre expérimenté

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Points : 1 543
    Points
    1 543
    Par défaut
    Salut,

    Citation Envoyé par poukill Voir le message
    Comment être sûr qu'on a pas récupèrer des octets en plus ou moins?
    C'est la couche d'en-dessous (ici Boost.Asio) qui te garantit ça puisque tu lui demandes de te réveiller après avoir reçu un certains nombre d'octets et pas avant ni après.

    MAT.

  6. #6
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Citation Envoyé par Mat007 Voir le message
    Salut,
    C'est la couche d'en-dessous (ici Boost.Asio) qui te garantit ça puisque tu lui demandes de te réveiller après avoir reçu un certains nombre d'octets et pas avant ni après.
    MAT.
    Là je parlais du cas où justement on n'envoie PAS la taille des données avant les données elles-mêmes!
    Sinon oui, je suis d'accord avec toi dans le cas <longueur><valeur>

  7. #7
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Du coup, je vais rester sur mon idée de <longueur><valeur>.
    Mais par contre, j'aimerai toujours savoir comment la première méthode que j'ai évoquée dans mon post du haut (cf tuto de Khayyam) peut fonctionner correctement??
    Si quelqu'un a la réponse...

  8. #8
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Quand on observe quelque peut le code de réception, on se rend compte que tu définis une chaine de caractères sur base du caractère qui commence à l'adresse de network_buffer[0] et contenant ... network_buffer.size() caractères...

    Nous sommes donc bien dans le cadre d'une réception <longueur> <donnée>, même si les valeurs sont inversées lors de la création de la chaine

    Il faudrait donc voir du coté de la classe qui est instanciée sous le nom de network_buffer mais, selon toute vraisemblance, lorsque asio envoie une chaine de caractère, elle fournit "d'office" la taille de celle-ci, et, lors de la réception, elle se base sur cette taille pour... récupérer l'ensemble de la chaine
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  9. #9
    Rédacteur

    Avatar de khayyam90
    Homme Profil pro
    Architecte de système d’information
    Inscrit en
    Janvier 2004
    Messages
    10 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Architecte de système d’information

    Informations forums :
    Inscription : Janvier 2004
    Messages : 10 369
    Points : 40 164
    Points
    40 164
    Par défaut
    Bien le bonjour,

    Dans le code, network_buffer est un vector<char>. La création de la socket définit un callback à appeler au réveil et la destination des octets reçus network_buffer.

    C'est Asio qui s'occupe de gérer les nombres d'octets envoyés, et qui s'occupe de découper les messages s'il y a besoin. A bas niveau les fonctions d'envoi et de réception nécessitent une taille pour dimensionner des buffers, Asio s'occupe de renseigner ces infos.

    L'un des avantages à utiliser une bibliothèque réseau de haut niveau est justement de ne plus avoir à se préoccuper de ces infos.

    L'exemple montré dans ce topic est basé sur du TCP, si on entre dans le callback de réveil, c'est soit que le message est bien arrivé et en entier, soit qu'il y a eu une erreur de transmission (auquel cas le asio::error_code a une valeur précise).

  10. #10
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Ok merci Koala et Khayyam... c'est plus clair
    Du coup, je comprend pas pourquoi boost nous montre dans son exemple de chat une structure de message à la sauce C (pas C++ pour un sou ), avec des champs bien spécifiés, etc...
    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
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
     
    class chat_message
    {
    public:
      enum { header_length = 4 };
      enum { max_body_length = 512 };
     
      chat_message()
        : body_length_(0)
      {
      }
     
      const char* data() const
      {
        return data_;
      }
     
      char* data()
      {
        return data_;
      }
     
      size_t length() const
      {
        return header_length + body_length_;
      }
     
      const char* body() const
      {
        return data_ + header_length;
      }
     
      char* body()
      {
        return data_ + header_length;
      }
     
      size_t body_length() const
      {
        return body_length_;
      }
     
      void body_length(size_t length)
      {
        body_length_ = length;
        if (body_length_ > max_body_length)
          body_length_ = max_body_length;
      }
     
      bool decode_header()
      {
        using namespace std; // For strncat and atoi.
        char header[header_length + 1] = "";
        strncat(header, data_, header_length);
        body_length_ = atoi(header);
        if (body_length_ > max_body_length)
        {
          body_length_ = 0;
          return false;
        }
        return true;
      }
     
      void encode_header()
      {
        using namespace std; // For sprintf and memcpy.
        char header[header_length + 1] = "";
        sprintf(header, "%4d", body_length_);
        memcpy(data_, header, header_length);
      }
     
    private:
      char data_[header_length + max_body_length];
      size_t body_length_;
    };
    Du coup, je reste perplexe. C'était juste pour montrer comment ça marche sous le capot? Ou pour montrer qu'on peut le faire ?

  11. #11
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Euh... Excusez moi de revenir à la charge, mais ... comprend pas !
    J'illustre mon propos avec un peu de code, reproduisant assez fidèlement le tuto de Khayyam.

    Côté serveur, j'attend en mode bloquant une connexion (j'ai fait simple). Lorsqu'un client se connecte, on réceptionne un message de sa part en mode asynchrone, avec un std::vector<char> comme dans le tuto de Khayyam.

    Côté client, je me connecte au serveur dont je connais l'IP. Puis j'envoie un message tapé par l'utilisateur en mode synchrone.
    Tout pareil quoi.

    He ben avec un std::vector bien vide... bah BOUM évidemment. Honnêtement je vois pas l'astuce là.
    Évidemment, en mettant un boost::array<char, 128> comme buffer de réception je récupère bien mes données, et j'ai accès au nombre de bits transférés donc je récupère mes moutons
    Mais lorsque je fais l'allocation du buffer, je ne connais pas la taille des données que je vais recevoir...

    J'aimerai bien quelques explications, surtout de la part de Khayyam qui semble avoir utilisé boost.Asio de cette façon !

    Code serveur :
    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
     
    #define _WIN32_WINNT 0x0501
     
    #include <iostream>
    #include <string>
    #include <boost/asio.hpp>
    #include <boost/bind.hpp>
     
    using boost::asio::ip::tcp;
     
    void handle_accept(const boost::system::error_code& error, size_t bytes_received)
    {
    	if (!error)
    	{
    		std::cout << "OK" << std::endl;
    	}
    }
     
    int main()
    {
    	boost::asio::io_service ios;
    	tcp::endpoint endpoint(tcp::v4(), 13);
    	tcp::acceptor acceptor(ios, endpoint);
    	tcp::socket sock(ios);
     
    	acceptor.accept(sock);
     
    	std::vector<char> network_buffer;
    	sock.async_receive(boost::asio::buffer(network_buffer),
    		boost::bind(&handle_accept, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)
    		);
     
    	ios.run();
     
    	return 0;
    }
    code client:
    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
     
    #define _WIN32_WINNT 0x0501
     
    #include <iostream>
    #include <string>
    #include <boost/asio.hpp>
    #include <boost/bind.hpp>
     
    int main()
    {
    	boost::asio::io_service ios;
    	boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("192.168.0.4"), 13);
    	boost::asio::ip::tcp::socket socket(ios);
     
    	std::string msg;
    	std::cout << "Message a transmettre?" << std::endl;
    	std::getline(std::cin, msg);
     
    	// Connection
    	socket.connect(endpoint);
     
    	socket.send(boost::asio::buffer(msg));
     
    	return 0;
    }

  12. #12
    Membre chevronné
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Points : 2 107
    Points
    2 107
    Par défaut
    Bon j'ai trouvé ma réponse dans un autre exemple de boost.
    Il faut donc bien, comme on pouvait s'y attendre, envoyer la taille des données avant d'envoyer la "purée" restante.
    L'exemple sur la sérialisation le montre bien.

    à ceux qui ont pris la peine de me répondre et à me pousser plus loin dans mes questions...

    A+,
    Poukill

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. envoi et réception des données via serial port
    Par azert2010 dans le forum C#
    Réponses: 2
    Dernier message: 09/05/2011, 23h53
  2. [MVC] gestion envoie et réception de donnée
    Par lequebecois79 dans le forum Spring Web
    Réponses: 0
    Dernier message: 14/03/2011, 20h35
  3. [AJAX] Envoi et réception de données XML avec AJAX
    Par darontankian dans le forum AJAX
    Réponses: 2
    Dernier message: 05/08/2010, 11h53
  4. [RCP] Envoi et réception des données entre view et window
    Par chiraz86 dans le forum Eclipse Platform
    Réponses: 0
    Dernier message: 25/03/2010, 20h01
  5. Envoi et réception de données : Communication Temps Réel
    Par mehdi_862000 dans le forum VC++ .NET
    Réponses: 8
    Dernier message: 26/05/2008, 14h14

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