« Fonctionnement d'un ordinateur/Les liaisons point à point » : différence entre les versions

Un livre de Wikilivres.
Contenu supprimé Contenu ajouté
m typo
Ligne 98 : Ligne 98 :
====Les protocoles à fenêtre glissante====
====Les protocoles à fenêtre glissante====


Les deux problèmes précédents peuvent être résolu en utilisant ce qu'on appelle une '''fenêtre glissante'''. Avec cette méthode, les trames sont envoyées les unes après les autres, sans attendre la réception des ACKs. Chaque trame est numérotée de manière à ce que l'émetteur et le récepteur puisse l’identifier. Lorsque le récepteur envoie les ACK, il précise le numéro de la trame dont il accuse la réception. Ce faisant, l'émetteur sait quelles sont les trames qui ont étés reçues et celles à renvoyer (modulo les time-out de chaque trame).
Les deux problèmes précédents peuvent être résolus en utilisant ce qu'on appelle une '''fenêtre glissante'''. Avec cette méthode, les trames sont envoyées les unes après les autres, sans attendre la réception des ACKs. Chaque trame est numérotée de manière à ce que l'émetteur et le récepteur puisse l’identifier. Lorsque le récepteur envoie les ACK, il précise le numéro de la trame dont il accuse la réception. Ce faisant, l'émetteur sait quelles sont les trames qui ont été reçues et celles à renvoyer (modulo les time-out de chaque trame).


On peut remarquer qu'avec cette méthode, les trames sont parfois reçues dans le désordre, alors qu'elles ont été envoyées dans l'ordre. Ce mécanisme permet donc de conserver l'ordre des données envoyées, tout en garantissant le fait que les données sont effectivement transmises sans problèmes. Avec cette méthode, l'émetteur va accumuler les trames à envoyer/déjà envoyées dans une mémoire. L'émetteur devra gérer deux choses : où se situe la première trame pour laquelle il n'a pas d'ACK, et la dernière trame envoyée. La raison est simple : la prochaine trame à envoyer est l'une de ces deux trames. Tout dépend si la première trame pour laquelle il n'a pas d'ACK est validée ou non. Si son ACK n'est pas envoyé, elle doit être renvoyée, ce qui demande de savoir quelle est cette trame. Si elle est validée, l'émetteur pourra envoyer une nouvelle trame, ce qui demande de savoir quelle est la dernière trame envoyée (mais pas encore confirmée). Le récepteur doit juste mémoriser quelle est la dernière trame qu'il a recue. Lui aussi va devoir accumuler les trames recues dans une mémoire, pour les remettre dans l'ordre.
On peut remarquer qu'avec cette méthode, les trames sont parfois reçues dans le désordre, alors qu'elles ont été envoyées dans l'ordre. Ce mécanisme permet donc de conserver l'ordre des données envoyées, tout en garantissant le fait que les données sont effectivement transmises sans problèmes. Avec cette méthode, l'émetteur va accumuler les trames à envoyer/déjà envoyées dans une mémoire. L'émetteur devra gérer deux choses : où se situe la première trame pour laquelle il n'a pas d'ACK, et la dernière trame envoyée. La raison est simple : la prochaine trame à envoyer est l'une de ces deux trames. Tout dépend si la première trame pour laquelle il n'a pas d'ACK est validée ou non. Si son ACK n'est pas envoyé, elle doit être renvoyée, ce qui demande de savoir quelle est cette trame. Si elle est validée, l'émetteur pourra envoyer une nouvelle trame, ce qui demande de savoir quelle est la dernière trame envoyée (mais pas encore confirmée). Le récepteur doit juste mémoriser quelle est la dernière trame qu'il a reçue. Lui aussi va devoir accumuler les trames reçues dans une mémoire, pour les remettre dans l'ordre.


<noinclude>
<noinclude>

Version du 9 juin 2021 à 09:55

On a vu dans le chapitre précédent qu'il faut distinguer les liaisons point à point des bus de communication. Dans ce chapitre, nous allons tout ce qui a trait aux liaisons point à point, à savoir comment les données sont transmises sur de telles liaisons, comment l'émetteur et le récepteur s'interfacent, etc. Gardez cependant à l'esprit que tout ce qui sera dit dans ce chapitre vaut aussi bien pour les liaisons point à point que pour les bus de communication. En effet, les liaisons point à point font face aux même problèmes que les bus de communication, si ce n'est la gestion de l'arbitrage.

Les codes en ligne : le codage des bits sur une ligne de transmission

Chaque fil d'un bus transmet un signal, qui peut être codé de diverses manières. Il existe des méthodes relativement nombreuses pour coder un bit de données pour le transmettre sur un bus : ces méthodes sont appelées des codages en ligne. Toutes codent celui-ci avec une tension, qui peut prendre un état haut (tension forte) ou un état bas (tension faible, le plus souvent proche de 0 volts). Outre le codage des données, il faut prendre aussi en compte le codage des commandes. En effet, certains bus série utilisent des fils dédiés pour la transmission des bits de données et de commande. Cela permet d'éviter d'utiliser trop de fils pour un même procédé.

Les codages non-différentiels

Pour commencer, nous allons voir les codages qui permettent de transférer un bit sur un seul fil (nous verrons que d'autres font autrement, mais laissons cela à plus tard). Il en existe de toutes sortes, qui se distinguent par des caractéristiques électriques qui sont à l’avantage ou au désavantage de l'un ou l'autre suivant la situation : meilleur spectre de bande passante, composante continue nulle/non-nulle, etc. Les plus courants sont les suivants :

  • Le codage NRZ-L utilise l'état haut pour coder un 1 et l'état bas pour le zéro (ou l'inverse).
  • Le codage RZ est similaire au codage NRZ, si ce n'est que la tension retourne systématiquement à l'état bas après la moitié d'un cycle d'horloge. Celui-ci permet une meilleure synchronisation avec le signal d'horloge, notamment dans les environnements bruités.
  • Le codage NRZ-M fonctionne différemment : un état haut signifie que le bit envoyé est l'inverse du précédent, tandis que l'état bas indique que le bit envoyé est identique au précédent.
  • Le codage NRZ-S est identique au codage NRZ-M si ce n'est que l'état haut et bas sont inversés.
  • Avec le codage Manchester, aussi appelé codage biphasé, un 1 est codé par un front descendant, alors qu'un 0 est codé par un front montant (ou l'inverse, dans certaines variantes).
Illustration des différents codes en ligne.

Faisons une petite remarque sur le codage de Manchester : il s'obtient en faisant un OU logique entre l'horloge et le flux de bits à envoyer (codé en NRZ-L). Les bits y sont donc codés par des fronts montants ou descendants, et l'absence de front est censé être une valeur invalide. Si je dis censé, c'est que de telles valeurs invalides peuvent avoir leur utilité, comme nous le verrons dans le chapitre sur la couche liaison. Elles peuvent en effet servir pour coder autre chose que des bits, comme des bits de synchronisation entre émetteur et récepteur, qui ne doivent pas être confondus avec des bits de données. Mais laissons cela à plus tard.

Codage de Manchester.

Les codages différentiels

Pour plus de fiabilité, il est possible d'utiliser deux fils pour envoyer un bit (sur un bus série). Ces deux fils ont un contenu qui est inversé électriquement : le premier utilise une tension positive pour l'état haut et le second une tension négative. Ce faisant, on utilise la différence de tension pour coder le bit, ce qui est plus fiable que d'utiliser des tensions deux fois plus élevées. Ce codage permet une meilleure résistance aux perturbations électromagnétiques, aux parasite et autres formes de bruits qui peuvent modifier les bits transmis. L'intérêt d'un tel montage est que les perturbations électromagnétiques vont modifier la tension dans les deux fils, la variation induite étant identique dans chaque fil. La différence de tension entre les deux fils ne sera donc pas influencée par la perturbation. Un tel codage est appelé un codage différentiel.

Évidemment, chaque codage a son propre version différentielle, à savoir avec deux fils de transmission.

Ce type de codage est, par exemple, utilisé sur le protocole USB. Sur ce protocole, deux fils sont utilisés pour transmettre un bit, via codage différentiel. Dans chaque fil, le bit est codé par un codage NRZ-L.

Signal USB : exemple.

Le codage des trames : début et de la fin de transmission

Deux composants électroniques communiquent entre eux en s'envoyant des trames, des paquets de bits où chaque information nécessaire à la transmission est à une place précise. Le terme trame réfère aux données transmises : toutes les informations nécessaires pour une transmission sont regroupées dans une trame, qui est envoyée telle quelle sur le bus. Le codage des trames indique comment l'émetteur doit envoyer les données sur le bus, ce qui permet aux récepteurs de détecter les données transmises et les interpréter. Il faut aussi que le récepteur puisse extraire des informations utiles du flux de bits transmis : quelle est l'adresse du récepteur, quand la transmission se termine-t-elle, et bien d'autres. Les transmissions sur un bus sont standardisées de manière à rendre l'interprétation du flux de bit claire et sans ambiguïté.

Sur les bus série, ces trames sont transmises bit par bit grâce à des circuits spécialisés : la trame est mémorisée dans un registre à décalage, qui envoie celle-ci bit par bit sur sa sortie (reliée au bus). Pour les bus parallèles, les choses peuvent être plus variables. Sur la plupart des bus parallèles, toute la trame est envoyée d'un coup sur le bus : cela simplifie fortement le codage de la trame, vu qu'il n'y a pas besoin de coder la longueur de la trame ou de préciser quand sa transmission est terminée. Mais il est possible d'envoyer des trames plus grosses que la largeur du bus : la trame est envoyée par paquets de données, octet par octet ou byte par byte.

La communication sur un bus série est légèrement différente de ce qu'on a vu dans les chapitres précédents. Auparavant, quand deux composants devaient communiquer, on se contentait de relier les deux par un fil, et faire en sorte que de nouvelles données soient envoyées à chaque cycle. Mais sur une liaison point à point, ce n'est pas toujours le cas : il arrive qu'elle soit inutilisée durant un certain temps, sans données transmises. Émetteur et récepteur doivent donc déterminer quand la liaison est inutilisée afin de ne pas confondre l'état de repos avec une transmission de données. Une transmission est un flux de bits qui a un début et une fin : le codage des trames doit indiquer quand commence une transmission et quand elle se termine. Le récepteur ne reçoit en effet qu'un flux de bits, et doit détecter le début et la fin des trames. Ce processus de segmentation d'un flux de bits en trames n’est cependant pas simple et l'émetteur doit fatalement ajouter des bits pour coder le début et la fin de la trame.

Ajouter un bit sur le bus de commande

Pour cela, on peut ajouter un bit au bus de commande, qui indique si le bus est en train de transmettre une trame ou s'il est inactif. Mais il y a moyen de se passer de ce genre d'artifice avec des méthodes plus ingénieuses.

Inactiver la liaison à la fin de l'envoi d'une trame

Une première solution est de laisser la liaison complètement inactive durant un certain temps, entre l'envoi de deux trames. La liaison reste à 0 Volts durant un temps fixe à la fin de l'émission d'une trame. Les composants détectent alors ce temps mort et en déduisent que l'envoi de la trame est terminée. Malheureusement, cette méthode pose quelques problèmes.

  • Premièrement, elle réduit les performances. Une bonne partie du débit binaire de la liaison passe dans les temps morts de fin de trame, lorsque la liaison est inactivée.
  • Deuxièmement, certaines trames contiennent de longues suites de 0, qui peuvent être confondues avec une liaison inactive.

Dans ce cas, le protocole de couche liaison peut résoudre le problème en ajoutant des bits à 1, dans les données de la trame, pour couper le flux de 0. Ces bits sont identifiés comme tel par l'émetteur, qui reconnait les séquences de bits problématiques.

Les octets START et STOP

De nos jours, la quasi-totalité des protocoles utilisent la même technique : ils placent un octet spécial (ou une suite d'octet) au début de la trame, et un autre octet spécial pour la fin de la trame. Ces octets de synchronisation, respectivement nommés START et STOP, sont standardisés par le protocole.

Problème : il se peut qu'un octet de la trame soit identique à un octet START ou STOP. Pour éviter tout problème, ces pseudo-octets START/STOP sont précédés par un octet d'échappement, lui aussi standardisé, qui indique qu'ils ne sont pas à prendre en compte. Les vrais octets START et STOP ne sont pas précédés de cet octet d'échappement et sont pris en compte, là où les pseudo-START/STOP sont ignorés car précédés de l'octet d'échappement. Cette méthode impose au récepteur d'analyser les trames, pour détecter les octets d'échappements et interpréter correctement le flux de bits reçu. Mais cette méthode a l'avantage de gérer des trames de longueur arbitrairement grandes, sans vraiment de limites.

Trame avec des octets d'échappement.

Une autre solution consiste à remplacer l'octet/bit STOP par la longueur de la trame. Immédiatement à la suite de l'octet/bit START, l'émetteur va envoyer la longueur de la trame en octet ou en bits. Cette information permettra au récepteur de savoir quand la trame se termine. Cette technique permet de se passer totalement des octets d'échappement : on sait que les octets START dans une trame sont des données et il n'y a pas d'octet STOP à échapper. Le récepteur a juste à compter les octets qu'il reçoit et 'a pas à détecter d'octets d'échappements. Avec cette approche, la longueur des trames est bornée par le nombre de bits utilisés pour coder la longueur. Dit autrement, elle ne permet pas de trames aussi grandes que possibles.

Trame avec un champ "longueur".

Dans le cas où les trames ont une taille fixe, à savoir que leur nombre d'octet ne varie pas selon la trame, les deux techniques précédentes sont inutiles. Il suffit d'utiliser un octet/bit de START, les récepteurs ayant juste à compter les octets envoyés à sa suite. Pas besoin de STOP ou de coder la longueur de la trame.

Les bits de START/STOP

Il arrive plus rarement que les octets de START/STOP soient remplacés par des bits spéciaux ou une séquence particulière de fronts montants/descendants.

Une possibilité est d'utiliser les propriétés certains codages, comme le codage de Manchester. Dans celui-ci, un bit valide est représenté par un front montant ou descendant, qui survient au beau milieu d'une période. L'absence de fronts durant une période est censé être une valeur invalide, mais les concepteurs de certains bus ont décidé de l'utiliser comme bit de START ou STOP. Cela donne du sens aux deux possibilités suivantes : la tension reste constante durant une période complète, soit à l'état haut, soit à l'état bas. Cela permet de coder deux valeurs supplémentaires : une où la tension reste à l'état haut, et une autre où la tension reste à l'état bas. La première valeur sert de bit de START, alors que l'autre sert de bit de STOP. Cette méthode est presque identique aux octets de START et de STOP, sauf qu'elle a un énorme avantage en comparaison : elle n'a pas besoin d'octet d'échappement dans la trame, pas plus que d'indiquer la longueur de la trame.

Un autre exemple est celui des bus RS-232, RS-485 et I²C, où les bits de START et STOP sont codés par des fronts sur les bus de données et de commande.

La fiabilité des transmissions sur une liaison point à point et/ou un bus

ACK TCP

Lorsqu'une trame est envoyée, il se peut qu'elle n'arrive pas à destination correctement. Des parasites peuvent déformer la trame et/ou en modifier des bits au point de la rendre inexploitable. Dans ces conditions, il faut systématiquement que l'émetteur et le récepteur détectent l'erreur : ils doivent savoir que la trame n'a pas été transmise ou qu'elle est erronée.

Les techniques de détection et de correction d'erreurs

Pour cela, il existe diverses méthodes de de détection et de correction d'erreur, que nous avons abordées en partie dans les premiers chapitres du cours. On en distingue deux classes : celles qui ne font que détecter l'erreur, et celles qui permettent de la corriger. Tous les codes correcteurs et détecteurs d'erreur ajoutent tous des bits aux données de base, ces bits étant appelés des bits de correction/détection d'erreur. Ces bits servent à détecter et éventuellement corriger toute erreur de transmission/stockage. Plus le nombre de bits ajoutés est important, plus la fiabilité des données sera importante. Dans le cas le plus simple, on se contente d'un simple bit de parité. Dans d'autres cas, on peut ajouter une somme de contrôle ou un code de Hamming à la trame. Cela permet de détecter les erreurs de transmission.

Les méthodes de retransmission

Si l'erreur peut être corrigée par le récepteur, tout va bien. Mais il arrive souvent que ce ne soit pas le cas : l'émetteur doit alors être prévenu et agir en conséquence. Pour cela, le récepteur peut envoyer une trame à l'émetteur qui signifie : la trame précédente envoyée est invalide. Cette trame est appelée un accusé de non-réception. La trame fautive est alors renvoyée au récepteur, en espérant que ce nouvel essai soit le bon. Mais cette méthode ne fonctionne pas si la trame est tellement endommagée que le récepteur ne la détecte pas. Pour éviter ce problème, on utilise une autre solution, beaucoup plus utilisée dans le domaine du réseau. Celle-ci utilise des accusés de réception, à savoir l'inverse des accusés de non-réception. Ces accusés de réception sont envoyés à l'émetteur pour signifier que la trame est valide et a bien été reçue. Nous les noterons ACK dans ce qui suivra.

Après avoir envoyé une trame, l'émetteur va attendra un certain temps que l'ACK correspondant lui soit envoyé. Si l’émetteur ne reçoit pas d'ACK pour la trame envoyée, il considère que celle-ci n'a pas été reçue correctement et la renvoie. Pour résumer, on peut corriger et détecter les erreurs avec une technique qui mélange ACK et durée d'attente : après l'envoi d'une trame, on attend durant un temps nommé time-out que l'ACK arrive, et on renvoie la trame au bout de ce temps si non-réception. Cette technique porte un nom : on parle d'Automatic repeat request.

Le protocole Stop-and-Wait

Dans le cas le plus simple, les trames sont envoyées unes par unes au rythme d'une trame après chaque ACK. En clair, l'émetteur attend d'avoir reçu l'ACK de la trame précédente avant d'en envoyer une nouvelle. Parmi les méthodes de ce genre, la plus connue est le protocole Stop-and-Wait.

Cette méthode a cependant un problème pour une raison simple : les trames mettent du temps avant d'atteindre le récepteur, de même que les ACK mettent du temps à faire le chemin inverse. Une autre conséquence des temps de transmission est que l'ACK peut arriver après que le time-out (temps d'attente avant retransmission de la trame) soit écoulé. La trame est alors renvoyée une seconde fois avant que son ACK arrive. Le récepteur va alors croire que ce second envoi est en fait l'envoi d'une nouvelle trame !

Pour éviter cela, la trame contient un bit qui est inversé à chaque nouvelle trame. Si ce bit est le même dans deux trames consécutives, c'est que l'émetteur l'a renvoyée car l'ACK était en retard. Mais les temps de transmission ont un autre défaut avec cette technique : durant le temps d'aller-retour, l'émetteur ne peut pas envoyer de nouvelle trame et doit juste attendre. Le support de transmission n'est donc pas utilisé de manière optimale et de la bande passante est gâchée lors de ces temps d'attente.

Les protocoles à fenêtre glissante

Les deux problèmes précédents peuvent être résolus en utilisant ce qu'on appelle une fenêtre glissante. Avec cette méthode, les trames sont envoyées les unes après les autres, sans attendre la réception des ACKs. Chaque trame est numérotée de manière à ce que l'émetteur et le récepteur puisse l’identifier. Lorsque le récepteur envoie les ACK, il précise le numéro de la trame dont il accuse la réception. Ce faisant, l'émetteur sait quelles sont les trames qui ont été reçues et celles à renvoyer (modulo les time-out de chaque trame).

On peut remarquer qu'avec cette méthode, les trames sont parfois reçues dans le désordre, alors qu'elles ont été envoyées dans l'ordre. Ce mécanisme permet donc de conserver l'ordre des données envoyées, tout en garantissant le fait que les données sont effectivement transmises sans problèmes. Avec cette méthode, l'émetteur va accumuler les trames à envoyer/déjà envoyées dans une mémoire. L'émetteur devra gérer deux choses : où se situe la première trame pour laquelle il n'a pas d'ACK, et la dernière trame envoyée. La raison est simple : la prochaine trame à envoyer est l'une de ces deux trames. Tout dépend si la première trame pour laquelle il n'a pas d'ACK est validée ou non. Si son ACK n'est pas envoyé, elle doit être renvoyée, ce qui demande de savoir quelle est cette trame. Si elle est validée, l'émetteur pourra envoyer une nouvelle trame, ce qui demande de savoir quelle est la dernière trame envoyée (mais pas encore confirmée). Le récepteur doit juste mémoriser quelle est la dernière trame qu'il a reçue. Lui aussi va devoir accumuler les trames reçues dans une mémoire, pour les remettre dans l'ordre.