12.5. Fiabilité – séquences, ACK, retransmissions

La couche de tramage détecte les corruptions grâce à ses CRC. La couche de fiabilité transforme la « corruption détectée » en « l’application ne voit jamais de données corrompues » en négociant des retransmissions chaque fois qu’un paquet n’arrive pas intact.

12.5.1. Numéros de séquence

Chaque en-tête de paquet porte un numéro de séquence d’un octet, distinct pour chaque sens de circulation. L’expéditeur incrémente le compteur avant la transmission ; le récepteur vérifie que la séquence de chaque paquet reçu correspond à la précédente plus un (modulo 256).

Trois cas peuvent se présenter au récepteur à la place d’un paquet propre et dans l’ordre :

  • Le numéro de séquence attendu, avec un CRC valide. Le paquet est transmis à la couche supérieure.

  • Le numéro de séquence attendu, avec un CRC erroné. Le récepteur rejette le paquet et (si les ACK sont négociés) envoie un NAK demandant une retransmission.

  • Un numéro de séquence supérieur d’une unité à celui attendu, avec un CRC valide. Le récepteur sait que le paquet précédent a été perdu ; il envoie un NAK référençant la séquence manquante et met le nouveau paquet de côté.

Le cas du doublon (une retransmission arrivant après que l’original a finalement été reçu) est traité en comparant au compteur attendu : si la séquence est en retard par rapport à celle attendue, le paquet est un doublon et le récepteur le supprime après avoir renvoyé l’ACK que l’expéditeur n’a manifestement pas reçu la première fois.

12.5.2. ACK et NAK

Deux bits de drapeau dans l’en-tête du paquet portent le trafic de fiabilité lui-même :

  • ACK_REQ positionné sur un paquet sortant signifie « je veux un accusé de réception en retour ». Les paquets de données le positionnent normalement ; les pings d’état et les événements ponctuels peuvent s’en passer.

  • ACK positionné sur un paquet signifie « ce paquet est l’accusé de réception du numéro de séquence indiqué dans l’en-tête ». Il ne porte aucune charge utile propre.

  • NAK positionné signifie « ce paquet en rejette un précédent » – généralement à cause d’un CRC erroné ou d’un trou dans les numéros de séquence. L’en-tête indique à l’expéditeur quelle séquence retransmettre.

L’expéditeur exécute une boucle d’arrêt et d’attente (stop-and-wait) : il transmet un paquet qui exige un accusé de réception, puis attend l’ACK (ou le NAK) correspondant avant d’envoyer le suivant. Le modèle à un seul paquet en vol maintient l’état de l’expéditeur borné – quelques centaines d’octets sur les plus petites caméras – et correspond au rôle du protocole de canal de contrôle entre deux extrémités plutôt que de pipeline optimisé pour le débit. Sur un NAK, l’expéditeur retransmet le même paquet avec le drapeau RTX positionné afin que le récepteur sache qu’il s’agit d’une nouvelle tentative.

12.5.3. Délai de retransmission

Si ni ACK ni NAK n’arrive dans le délai de retransmission, l’expéditeur retransmet de lui-même le paquet en vol. Le délai vaut par défaut 500 ms et double à chaque tentative consécutive (1 s, 2 s, …). Après le nombre configuré de tentatives – trois par défaut – l’expéditeur abandonne et signale une erreur de transport à l’application.

Le doublement du délai correspond au schéma standard de temporisation exponentielle (exponential backoff). Un premier délai court permet de détecter rapidement les paquets perdus ; le doublement signifie qu’un hôte occupé pendant quelques centaines de millisecondes ne déclenche pas une avalanche de doublons qui aggraverait la charge.

12.5.4. Configurer la fiabilité

Les deux extrémités peuvent désactiver d’un commun accord des parties de la couche de fiabilité, lorsque l’application peut se permettre de perdre des données :

  • protocol.init(ack=False) désactive les ACK par paquet. L’expéditeur envoie et oublie ; le récepteur transmet ce qui arrive. Idéal pour la diffusion en continu de données de capteur où un échantillon périmé est acceptable.

  • protocol.init(seq=False) désactive le suivi des numéros de séquence, ce qui implique aussi la désactivation des ACK. Utile uniquement sur des transports parfaitement fiables.

  • protocol.init(crc=False) désactive la validation CRC mais laisse le reste du tramage intact. À ne faire que lorsque le transport lui-même est suffisamment robuste pour que les erreurs CRC ne se produisent pas.

Les valeurs par défaut – tout activé – constituent le bon point de départ pour toute session de débogage entre hôte et caméra. Une fois l’application en production, les compromis deviennent spécifiques à ses données et à son transport.

12.5.5. Les codes d’état

Lorsqu’une erreur de transport remonte jusqu’au code applicatif, elle arrive sous forme de code d’état. La bibliothèque du protocole en définit dix :

  • SUCCESS – l’opération s’est terminée.

  • FAILED – la commande a échoué pour une raison non précisée.

  • INVALID – le récepteur a rejeté la commande ou l’un de ses arguments.

  • TIMEOUT – un minuteur de nouvelle tentative a expiré.

  • BUSY – la caméra est occupée (généralement un canal verrouillé).

  • CHECKSUM – le CRC de l’en-tête ou de la charge utile ne correspondait pas.

  • SEQUENCE – le numéro de séquence était désordonné au-delà de ce que la couche peut récupérer.

  • OVERFLOW – une charge utile a dépassé le maximum négocié.

  • FRAGMENT – un message multi-fragments est arrivé avec des morceaux manquants.

  • UNKNOWN – un fourre-tout défensif pour les conditions véritablement inattendues.

Le code hôte appelant channel_read() les voit comme des exceptions Python ; le code applicatif côté caméra qui a choisi une gestion d’erreur personnalisée les voit comme des valeurs de retour des fonctions de rappel du backend. La plupart des applications de caméra n’ont pas du tout besoin de consulter les codes d’état – la bibliothèque gère les nouvelles tentatives, et seules les défaillances véritablement irrécupérables (par exemple, le transport lui-même a disparu) atteignent l’application.

Une fois le tramage en place pour détecter les corruptions et la fiabilité en place pour les corriger, le travail au niveau de la liaison est terminé. Le code applicatif voit des paquets tramés, ordonnés et intacts ; les octets qu’ils contiennent sont libres de signifier ce que le canal supérieur veut qu’ils signifient.