11.13 SocketServer ---
Un cadre pour les serveurs réseau
Le module SocketServer simplifie la tâche d'écriture de serveurs réseau.
Il y a quatre classes de serveur de base: TCPServer utilise le protocole Internet TCP, qui fournit des flux de données continues entre le client et le serveur. UDPServer utilise des datagrammes, des paquets discrets d'information qui peuvent arriver en désordre ou se perdre pendant le transit. Les classes plus rarement utilisées UnixStreamServer et UnixDatagramServer sont similaires, mais utilisent les sockets du domaine Unix; elles ne sont pas disponibles sur les plateformes non-Unix. Pour plus de détails sur la programmation réseau, consulter un livre comme UNIX Network Programming de W. Richard Steven, ou Win32 Network Programming de Ralph Davis.
Ces quatre classes traitent les requêtes en mode synchrone; chaque requête doit avoir été traitée avant que la requête suivante ne puisse l'être. Ceci ne convient pas si chaque requête a un temps de traitement élevé, parce qu'elle nécessite de nombreux traitements, ou parce qu'elle renvoie un volume de données important que le client met du temps à traiter. La solution consiste à créer un processus ou un thread séparé pour traiter chaque requête; les classes incorporées ForkingMixIn et ThreadingMixIn peuvent servir à prendre en charge un comportement asynchrone.
La création d'un serveur nécessite plusieurs étapes. D'abord, vous devez créer une classe de traitement des requêtes en dérivant la classe BaseRequestHandler et en surchargeant sa méthode handle(); cette méthode traitera les requêtes qui arrivent. Ensuite, vous devez instancier une des classes de serveur, en lui passant l'adresse du serveur et la classe de traitement des requêtes. Enfin, appelez la méthode handle_request() ou serve_forever() de l'objet serveur pour traiter une ou plusieurs requêtes.
Les classes de serveurs ont les mêmes méthodes et attributs publics, quel que soit le protocole réseau qu'elles utilisent:
fileno()
- Retourne un entier descripteur de fichier pour la socket sur laquelle le serveur écoute. Cette fonction est la plupart du temps passée à select.select(), pour permettre de gérer plusieurs serveurs dans le même processus.
handle_request()
- Traite une requête unique. Cette fonction appelle les méthodes suivantes dans l'ordre: get_request(), verify_request(), et process_request(). Si la méthode handle() fournie par l'utilisateur dans la classe de traitement déclenche une exception, la méthode handle_error() du serveur sera appelée.
serve_forever()
- Gère un nombre infini de requêtes. Appelle simplement handle_request() à l'intérieur d'une boucle sans fin.
address_family- La famille des protocoles à laquelle la socket du serveur appartient. socket.AF_INET et socket.AF_UNIX sont deux valeurs possibles.
RequestHandlerClass- La classe de traitement des requêtes fournie par l'utilisateur; une instance de cette classe est créée pour chaque requête.
server_address- L'adresse sur laquelle le serveur écoute. Le format des adresses varie avec la famille de protocole; voir la documentation du module socket pour les détails. Pour les protocoles Internet, c'est un tuple qui contient l'adresse et un numéro de port entier:
('127.0.0.1', 80), par exemple.
socket- L'objet socket sur lequel le serveur sera en écoute de requêtes entrantes.
Les classes du serveur prennent en charge les variables de classe suivantes:
request_queue_size- La taille de la file d'attente des requêtes. Si le traitement d'une requête unique dure longtemps, toute requête arrivant alors que le serveur est occupé est placée dans une file d'attente, jusqu'à request_queue_size requêtes. Quand la file d'attente est pleine, les requêtes ultérieures de clients recevront une erreur ``Connection denied'' (connexion refusée). La valeur par défaut est habituellement 5, mais on peut la modifier dans des sous-classes.
socket_type- Le type de socket utilisé par le serveur; socket.SOCK_STREAM et socket.SOCK_DGRAM sont deux valeurs possibles.
De nombreuses méthodes de serveur peuvent être surchargées par des sous-classes de classes de serveur de base comme TCPServer; ces méthodes n'ont pas d'utilité pour des utilisateurs externes de l'objet serveur.
finish_request()
- Traite effectivement la requête en instanciant RequestHandlerClass et en appelant sa méthode handle().
get_request()
- Doit accepter une requête de la socket, et retourner un couple contenant l'objet socket nouveau à utiliser pour communiquer avec le client, et l'adresse du client.
handle_error(requete, adresse_client)
- Cette fonction est appelée si la méthode handle() de RequestHandlerClass déclenche une exception. L'action par défaut est d'imprimer la trace d'erreur sur la sortie standard et de continuer de traiter les requêtes ultérieures.
process_request(requete, adresse_client)
- Appelle finish_request() pour créer une instance de RequestHandlerClass. Au besoin, cette fonction peut créer un nouveau processus ou un nouveau thread pour traiter la requête; c'est ce que font les classes ForkingMixIn et ThreadingMixIn.
server_activate()
- Appelée par le constructeur du serveur pour activer le serveur. Peut être surchargée.
server_bind()
- Appelée par le constructeur du serveur pour relier la socket à l'adresse désirée. Peut être surchargée.
verify_request(requete, adresse_client)
- Doit retourner une valeur booléenne; si la valeur est vrai, la requête sera traitée, si elle est faux, la requête sera refusée.
Cette fonction peut être surchargée pour implémenter un contrôle d'accès au serveur. L'implémentation par défaut renvoie toujours vrai.
La classe de traitement des requêtes doit définir une nouvelle méthode handle(), et peut surcharger les méthodes suivantes. Une nouvelle instance est créée pour chaque requête.
finish()
- Appelée après la méthode handle() pour exécuter toute action de nettoyage souhaitée. L'implémentation par défaut ne fait rien. Si setup() ou handle() déclenche une exception, cette fonction ne sera pas appelée.
handle()
- Cette fonction doit effectuer tout le travail requis pour servir une requête. Elle dispose de plusieurs attributs d'instance; la requête est disponible par self.request; l'adresse du client par self.client_address; et l'instance de serveur par self.server, au cas où elle aurait besoin d'accéder à des informations sur le serveur.
Le type de self.request est différent pour les services de datagrammes ou de flux. Pour les services de flux, self.request est un objet socket; pour les services de datagrammes, self.request est une chaîne de caractères.
Cependant, ceci peut être caché en utilisant les classes de traitement de requêtes incorporées StreamRequestHandler ou DatagramRequestHandler, qui surchargent les méthodes setup() et finish(), et fournissent les attributs self.rfile et self.wfile.
self.rfile et self.wfile peuvent servir respectivement à lire ou à écrire, pour obtenir les données de requête ou retourner des données au client.
setup()
- Appelée avant la méthode handle() pour exécuter toute action d'initialisation requise. L'implémentation par défaut ne fait rien.