FAQ Qt FAQ Qt Creator FAQ PyQt & PySide

FAQ QtConsultez toutes les FAQ

Nombre d'auteurs : 26, nombre de questions : 298, dernière mise à jour : 6 mai 2017 

 
OuvrirSommaireModulesQt NetworkSockets

Une erreur (très) courante est de croire qu'on recevra systématiquement le même nombre de paquets que le nombre de ceux qu'on a envoyés.

Un exemple concret : l'ordinateur A (émetteur) fait trois appels successifs à la fonction write() pour envoyer des données à l'ordinateur B (récepteur) :

  • 1er appel, il écrit 5 octets (ABCDE) ;
  • 2e appel, il écrit 2 octets (FG) ;
  • 3e appel, il écrit 3 octets (HIJ).

On sait que, chaque fois que B va recevoir des données, il émettra le signal readyRead() et que le slot qui y est connecté pourra appeler une fonction, du type readAll() pour récupérer tout ce qu'on vient de recevoir.

La première idée qui vient à l'esprit c'est que, comme A a appelé trois fois la fonction write(), B émettra 3 fois readyRead() et que les trois appels à readAll() retourneront ABCDE puis FG puis HIJ.

Cette manière de penser ne s'applique pas toujours.

En effet, il se peut très bien que l'on reçoive le tout :

  • en une seule fois : une seule émission du signal readyRead() et readAll() retourne ABCDEFGHIJ ;
  • en 4 fois : 4 émissions de readyRead() et readAll() retourne AB puis C puis DEFGH puis IJ ;
  • en 3 fois, en 10 fois, etc.

Il y a plusieurs raisons à cela.

  • Pour les gros paquets, les couches réseau inférieures à TCP (comme Ethernet) ne savent pas transporter des paquets d'une taille trop grande (1500 octets, en-têtes compris par défaut pour Ethernet). Donc l'envoi d'un seul gros paquet de données a toutes les chances d'être subdivisé en plusieurs paquets et donc d'être reçu en plusieurs fois côté récepteur. Or, la taille maximale d'un paquet avant qu'il ne soit découpé ne peut être prévue à l'avance : elle dépend de la configuration de l'ensemble des routeurs situés entre l'émetteur et le récepteur. De plus, pendant une même connexion, il se peut que les paquets empruntent des chemins différents, donc passent par des routeurs différents... La taille maximale peut varier dans le temps et ne peut être prévue a priori.
  • Les petits paquets, à l'inverse peuvent se voir regroupés en un seul paquet : si l'émetteur veut envoyer une dizaine de petits paquets dans un intervalle de temps très court (exemple : dix appels à write() successifs avec 2 octets écrits à chaque appel). L'interface réseau peut optimiser leur émission en les envoyant tous d'un coup pour économiser de la bande passante. En effet, un paquet TCP est typiquement composé d'un en-tête (origine, destination, port...) d'environ 20 octets suivi des données utiles (2 octets dans notre exemple). Au lieu d'envoyer 10 paquets, donc 10 en-têtes et 10 segments contenant des données, soit 10 x (20+2) = 220 octets, il va envoyer un seul paquet avec toutes les données, ce qui fera en tout un seul en-tête et 10x2 octets de données, soit 40 octets, ce qui représente une économie de plus de 80 % de bande passante. Ce mécanisme d'optimisation s'appelle l'algorithme de Nagle et peut être désactivé pour des besoins spécifiques.

En résumé, les seules choses que TCP est capable de garantir sont que :

  1. quoi qu'il arrive, tous les paquets seront transmis (sauf si bien entendu entre temps un des deux ordinateurs se déconnecte). Par contre, le temps de transmission n'est pas garanti. Cela peut prendre 1 microseconde comme une heure ;
  2. quoi qu'il arrive, l'ordre de réception sera le même que celui d'émission (A, puis B, puis C, etc.) ;
  3. quoi qu'il arrive, les données reçues seront strictement identiques à celles émises (on ne recevra pas N à la place de B).
Mis à jour le 7 mai 2012  par nouknouk

Lien : Quelles solutions pour pouvoir différencier mes blocs de données ?

On constate que l'on ne peut garantir que le nombre de paquets reçus sera toujours égal au nombre de paquets émis.

En effet, même en désactivant l'algorithme de Nagle, on garantit que le nombre de paquets reçus sera au moins égal au nombre de paquets émis, mais rien ne nous garantit que les routeurs intermédiaires ne vont pas fragmenter un paquet en plusieurs paquets.

Il n'existe pas de solution native pour gérer les blocs de données. Il y a deux solutions relativement simples et classiques qui conviennent dans la majorité des cas.

  • Pour chaque bloc de données que l'émetteur envoie, on va systématiquement le terminer avec un ou plusieurs caractères supplémentaires qui annonceront la fin du bloc (un marqueur).

Par exemple, en admettant qu'on transmette du texte, on décide que le marqueur sera le caractère de retour à la ligne (\n'). Côté récepteur, quand on reçoit les données, on les stocke dans un buffer jusqu'à ce qu'on trouve le marqueur (\n'). Quand on l'a trouvé, on prend tous les caractères reçus jusque là, et on a un bloc entier de données. Les caractères qui suivront représenteront le début du bloc suivant.

Cette méthode a l'avantage d'être très simple. Son principal inconvénient est qu'il faille trouver un marqueur qu'on est certain de ne jamais voir apparaître dans les vraies données. En effet, l'exemple ci-dessus fonctionne parfaitement… tant que les blocs de données eux-mêmes ne contiennent pas de retour chariot.

  • La technique de prédéclaration de la taille du paquet : lorsque l'émetteur désire envoyer un bloc de données, il va commencer par calculer la taille totale du bloc en octets, puis va écrire cette taille sur le socket (par exemplee sous la forme de 4 octets représentant un int) avant d'écrire le bloc de données lui-même.

Côté récepteur, on saura qu'au début de chaque nouveau bloc, on recevra d'abord sa taille sur 4 octets. Une fois la taille reçue et décodée, on prépare un buffer de la bonne taille pour les données à recevoir. Tant que le buffer n'est pas rempli, on ajoute tout ce qu'on reçoit dedans. Dès que le buffer est rempli, on sait qu'on a reçu un nouveau bloc de données. On peut alors le traiter et recommencer à chercher à décoder la taille du bloc suivant.

Mis à jour le 7 mai 2012  par nouknouk

Lien : Pourquoi les nombres de paquets émis et reçus avec QTcpSocket diffèrent-ils ?

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006 - 2017 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.