Blog ENI : Toute la veille numérique !
En raison d'une opération de maintenance, le site Editions ENI sera inaccessible le mardi 10 décembre, en début de journée. Nous vous invitons à anticiper vos achats. Nous nous excusons pour la gêne occasionnée
En raison d'une opération de maintenance, le site Editions ENI sera inaccessible le mardi 10 décembre, en début de journée. Nous vous invitons à anticiper vos achats. Nous nous excusons pour la gêne occasionnée
  1. Livres et vidéos
  2. Python 3
  3. Programmation asynchrone : avancée
Extrait - Python 3 Traitement de données et techniques de programmation (2e édition)
Extraits du livre
Python 3 Traitement de données et techniques de programmation (2e édition) Revenir à la page d'achat du livre

Programmation asynchrone : avancée

Transports et protocoles

1. Introduction

Ces notions de transport et de protocole (et on pourrait rajouter la notion de flux) sont des notions de bas niveau. Ces notions sont faites pour permettre d’automatiser des implémentations haute-performance de cas pratiques asynchrones usuels, comme on le verra plus tard pour du TCP, de l’UDP ou de l’appel de processus externe.

Les objets Transport ou Protocol ne sont pas faits pour être créés et gérés directement dans des scripts ou des applications, mais plutôt conçus comme des outils à destination de bibliothèques qui fourniront des outils de haut niveau, comme par exemple autobahn présenté dans le chapitre Programmation réseau.

Nous allons essayer de présenter ici les concepts généraux, sachant que la partie sur laquelle on insistera le plus sera celle présentant les transports et protocoles déjà implémentés par le langage.

Les transports sont faits pour décrire la manière dont les données sont transmises. Du moment que l’on parle de transmission de donnée à bas niveau, on parle toujours d’octets. Les transports décriront donc comment transporter des octets.

Les protocoles sont faits pour décrire quelles sont les données à transmettre, dans quel ordre les transmettre, quand les transmettre. En général, ils implémentent des RFC (spécifications génériques) et peuvent donc être potentiellement très complexes. Chaque instance d’un transport sera associée à une et une seule instance de protocole.

2. Transports

Les transports sont des classes fournies par asyncio permettant de créer une abstraction d’un canal de communication. Les transports ne sont généralement pas instanciés par vous, mais par la boucle d’événement. Cette dernière va initier le transport puis le canal de communication sous-jacent.

Un transport a pour vocation d’être utilisé par un protocole et à ce titre, une instance de transport est toujours associée à une instance de protocole, les deux travaillent de concert.

Pour commencer, nous allons importer la classe de base :

>>> from asyncio import BaseTransport 

Pour créer...

Flux

1. Introduction

Les flux sont un outil de plus bas niveau que les protocoles et les transports que nous avons présentés précédemment. Ils permettent de manipuler directement les données échangées. Un flux est unidirectionnel et peut être soit en lecture, soit en écriture. Pour avoir l’équivalent d’un transport entre, par exemple, un client et un serveur, il faut mettre en place un flux qui soit en lecture pour le client et en écriture pour le serveur ainsi qu’un autre flux qui soit en écriture pour le client et en lecture pour le serveur.

Il est conseillé d’utiliser préférentiellement les protocoles, mais dans certains cas, l’utilisation d’un simple flux peut s’avérer utile. Nous allons donc présenter la manière de les utiliser. 

2. Exemple avec le protocole TCP

Nous allons ici écrire un nouveau serveur TCP qui renvoie ce qu’il reçoit, mais en utilisant les flux. Voici la fonction qui effectue ce travail :

async def echo(reader, writer): 
    address = writer.get_extra_info("peername") 
    logger = logging.getLogger(f"Client {address}") 
    coloredlogs.install(fmt="%(name)s: %(message)s", level="DEBUG", 
logger=logger) 
    logger.debug("connection made {address}") 
 
    while True: 
        data = await reader.read(128) 
        if data: 
            logger.debug(f"received {data!r}") ...