P
'
t
i
t
e
C
h
a
t
t
e
 
spacer~ THE TROUBLE WITH LIFE IS THERE'S NO BACKGROUND MUSIC Articles | Connexion
 
~Programmation réseau en Python

Précédent  
  Python  
  Suivant
 Présentation

Pour permettre aux auteurs de logiciels de développer des applications aptes à communiquer sur un réseau informatique, Python propose une bibliothèque spécialisée de bas niveau. Vous trouverez celle-ci dans le module socket.
 Sommaire


 Introduction

La communication entre deux machines par l'intermédiaire d'un réseau s'effectue grâce à des structures particulières intitulées socket. Ils désignent des points d'entrée/sortie sur le réseau, liés à une adresse IP et à un numéro de port. Nous pourrons distinguer de fait deux sortes de socket : les clients et les serveurs. Un client peut simplement recevoir et émettre des informations tandis qu'un serveur possède des pouvoirs supplémentaires. Parmi ceux-ci, se trouve la capacité d'attendre la connexion d'un ou plusieurs clients.

En Python, les sockets prennent place dans les modules "socket" et "SocketServer". Bâtie au cour de ces modules, l'API réseau de Python se révèle être un simple portage des appels systèmes et des bibliothèques Unix sous une forme plus adaptée à la programmation objet. Néanmoins, les fonctions possèdent les mêmes noms que leurs équivalents C.

Les sockets se distinguent suivant deux caractéristiques, leur famille et leur type. La première d'entre elle peut correspondre à trois valeurs AF_UNIX, AF_INET et AF_INET6. Dans le cadre de développements orientés Internet, seules les deux dernières retiendront notre attention. La famille AF_INET désigne les sockets reposant sur le protocole IPv4 tandis que AF_INET6 désigne ceux basés sur IPv6. Ainsi, Python pourra constituer un langage idéal pour expérimenter la nouvelle version du protocole IP.

Nous devrons également choisir entre les deux types de sockets supportés : SOCK_STREAM et SOCK_DGRAM. Le premier type permet de manipuler des flux de données et le second des datagrammes. Enfin, quelle que soit la combinaison de famille/type définie, sachez qu'un nombre important d'options s'appliquent aux sockets et autorisent un contrôle plus fin des communications.


 Créer un socket client

La création d'un socket retourne au code appelant un objet correspondant au canal de communication. Pour ce faire, il convient d'invoquer la méthode socket() de la sorte :

from socket import *
sock = socket(AF_INET, SOCK_STREAM)
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
Le premier paramètre désigne la famille de socket et le second le type. Un troisième paramètre optionnel peut être précisé, il correspond au protocole à utiliser. Le module réseau en définit plusieurs tels que IPPROTO_ICMP ou IPPROTO_UDP. La plupart du temps, ce numéro de protocole est zéro (IPPROTO_IP), soit la valeur par défaut. Dans le doute, abstenez-vous de renseigner cet argument.

Une fois le socket créé, il convient de le connecter à un serveur, dans le cas d'un client, ou de le lier à une adresse dans le cas d'un serveur. La connexion s'effectue de la sorte :

sock.connect(('localhost', 2000))
       
      
JextCopier dans Jext
Nous représentons une adresse de connexion à l'aide d'un tuple contenant l'adresse du serveur, soit par son nom d'hôte complet (par exemple 'irc.helix.org') soit par son adresse IP (ici avec IPv4 '127.0.0.1'). Lorsque la connexion ne peut être établie, l'interpréteur lance une exception de type socket.error.

Par la suite, émettre et recevoir des données s'avère véritablement simple. Pour cela, deux méthodes retiendront notre attention : send() et recv(). L'émission doit traiter une chaîne de caractères. En réponse, nous recevons le nombre d'octets effectivement émis. Quand le trafic du réseau prend trop d'importance, il se peut que tous les octets de la chaîne ne soit pas émis, l'application doit donc en tenir compte. On peut cependant forcer l'émission de l'intégralité des données en exécutant sendall() à la place.



Principe de communication par sockets.

La méthode de réception des données, recv(), accepte comme argument un entier désignant le nombre d'octets maximal devant être lu dans le socket. Elle retourne une chaîne de caractères dont la longueur sera nécessairement inférieure ou égale à l'argument. Si aucune donnée ne peut être lue depuis le socket, l'appel à recv() interrompt l'exécution jusqu'à réception d'au moins un octet ou fermeture de la liaison.

En imaginant le port 2000 de la machine locale correspond à un serveur de type "echo", voici un petit code source permettant d'en éprouver l'efficacité :

sent = sock.send("Hello World\r\n")
data = sock.recv(sent)
print data
       
      
JextCopier dans Jext

 Création d'un socket serveur

La création d'un socket serveur se révèle similaire à celle d'un client. Ainsi, la méthode socket() doit se voir invoquée de la même manière et nous recevons un objet de même nature. Toutefois, nous n'appelons pas ensuite connect() mais bind(). Cette dernière méthode prend également en paramètre un tuple contenant l'adresse hôte et le port de communication. La spécification du premier paramètre n'a d'intérêt que si votre machine possède plusieurs noms, c'est-à-dire si elle se trouve connectée sur plusieurs réseaux. Couramment, nous spécifierons simplement une chaîne vide afin de désigner l'hôte locale.

Une fois le serveur créé, il convient de le placer en écoute de demandes de connexions. Pour ce faire, votre code source devra invoquer la méthode listen(). Celle-ci accepte un paramètre désignant la taille de la file d'attente. Le nombre maximal dépend du système. Pour terminer, il convient d'accepter les connexions grâce à la méthode accept(). Une fois exécutée, cette dernière renvoie un tuple contenant un socket de communication vers le client et un tuple caractérisant l'adresse du client.

Pour illustrer ceci, le listing 1 propose un code source implémentant un serveur identd minimal. Ce type de serveur est couramment employé pour ouvrir plusieurs connexions depuis sa machine sur un seul serveur IRC. Vous pourrez constater que la manipulation du socket issu de l'invocation de accept(), dénommé "irc" dans le code, se révèle équivalente à celle d'un socket client.


 Autres fonctions

Outre les fonctionnalités vitales abordées jusqu'à présent, les objets socket donnent accès à certains outils intéressants. Ainsi la méthode shutdown() permet de fermer les canaux de communication, indépendamment l'un de l'autre. L'instruction shutdown(0) ferme ainsi le canal de lecture du socket, shutdown(1) celui d'écriture et si vous utilisez la valeur 2 comme argument, les deux canaux seront fermés.

Nous pouvons également décider de rendre les processus de communication non bloquant. En écrivant sockobj.setblocking(0), nous modifions le comportement du socket. Ainsi, la méthode read() ne bloquera plus l'exécution dans l'attente de données mais générera à la place une exception.

Le module socket lui-même recèle quantités de fonctions très utiles pour la résolution de noms et d'adresses. Vous pourrez ainsi obtenir l'adresse IP de votre ordinateur par l'intermédiaire de l'instruction gethostbyname(gethostname()). Les fonctions ntohl() et htonl() vous rendront bien des services si vous développez des applications reposant sur des protocoles un peu complexes. Ces fonctions servent à convertir des entiers sur 32 bits depuis l'ordre réseau des octets vers celui de la machine et inversement. Vous trouverez sur le CD-Rom un exemple de client IRC faisant appel à l'ensemble des techniques discutées ici.

identd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
identd.bind(('', 113))
identd.listen(1)
irc, irchost = identd.accept()
match = re.search('(\d+)', irc.recv(512))
response = ""
if match is not None:
  response = match.group(1) + ', 6667 : USERID : UNIX : login\r\n'
else:
  response = '6667 : USERID : UNIX : login\r\n'
irc.send(response)
irc.close()
identd.close()
       
      
JextCopier dans Jext | Jext | Plugin Codegeek


par Romain Guy
romain.guy@jext.org
http://www.jext.org
Dernière mise à jour : 14/10/2006


Précédent  
  Python  
  Suivant

 
#ProgX©2005 Mathieu GINOD - Romain GUY - Erik LOUISE