S52 - TP3 - Les sockets en python
Le principe
Les sockets sont basés sur une architecture client/serveur: Le serveur décide d’accepter les demandes de connection sur un port particulier, tandis que le client demande une connexion sur le serveur.
Une fois la connexion établie, les deux programmes peuvent communiquer sur la socket ‘s’ à l’aide de lecture (en fait une réception ‘s.recv()') et d’écriture (en fait une émission : ‘s.send()').
Comprendre et créer un client
- client.py
#Un client http très simple import socket # un exemple d'interrogation d'un serveur DNS print("Interrogation du DNS: "+socket.gethostbyname("www.univ-tln.fr")) #1- Construire un objet qui représente la socket # vers laquelle le client veut se connecter s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #2- ouvrir la connection vers la socket s.connect(("www.univ-tln.fr",80)) #3- construire le message à envoyer (ici une requête http). request = "GET / HTTP/1.1\r\n" request+= "Host: www.univ-tln.fr\r\n" request+= "Connection: Close\r\n\r\n" #4- l'envoyer sur la socket s.send(request.encode('UTF-8')) #5- lire 15 octet sur la socket (augmenter pour lire plus...) data = s.recv(15) # convertir le tableau de 15 octets en une chaine de caractère # et l'afficher. print(data.decode('utf-8')) s.close()
Client Time
lsis.univ-tln.fr
.
Attention, il y a plusieurs difficultés :
- ce protocole retourne le temps sur 4octets. Il faut donc convertir le tableau de 4 octets lu en un entier.
Ce module https://docs.python.org/3/library/struct.html#module-struct offre les fonctions de conversion.
- pour assurer la portabilité de la représentation des nombres entre les différentes architectures, un standard est fixé sur le réseau, il faut donc faire des conversions avant écriture et après lecture sur la socket :
- socket.ntohl(x) : Convert 32-bit positive integers from network to host byte order. On machines where the host byte order is the same as network byte order, this is a no-op; otherwise, it performs a 4-byte swap operation.
- socket.htonl(x)
Convert 32-bit positive integers from host to network byte order. On machines where the host byte order is the same as network byte order, this is a no-op; otherwise, it performs a 4-byte swap operation.
- D’autres fonctions utiles : https://docs.python.org/2/library/socket.html
- il y a plusieurs habitudes différentes pour indiquer une date (en secondes depuis 1er janvier 1970, depuis le 1er janvier 1900 à minuit UTC, … Pour information, il y a 2208988800s entre les deux).
- on peut utiliser la fonction
time.ctime()
pour afficher de façon lisible la date. cf. https://docs.python.org/3/library/time.html pour les conversion de temps en python.
Client FTP
L’échange suivant montre un échange avec le protocole FTP (sur ftp.univ-tln.fr) pour se connecter et obtenir des informations sur le système.
220 mail1.univ-tln.fr FTP server (Version wu-2.6.2(1) Fri Feb 14 10:45:38 CET 2003) ready.
USER anonymous
331 Guest login ok, send your complete e-mail address as password.
PASS anonymous
230-Please read the file README
230- it was last modified on Fri Feb 14 11:16:03 2003 - 4248 days ago
230 Guest login ok, access restrictions apply.
SYST
215 UNIX Type: L8 Version: BSD-198911
L’idée est générale est d’utiliser un automate. Le programme lit sur la socket et en fonction de ce qu’il lit et de l’état dans lequel il se trouve et envoie le bon message : Lire les lignes à l’infini :
- Si je suis à l’état 0 et que je reçois une chaîne qui commence par “220”, j’envoie
USER …
et je passe à l’état 1. - Si je suis à l’état 1 …
- …
- Ne pour oublier de sortir et de déconnecter.
Pour faciliter le traitement des chaînes sur les sockets ce qui arrive très souvent :
- Il est possible de lire une socket comme un fichier texte avec
for line in s.makefile(‘r’):
- Les fonctions sur les chaînes sont rappelée ici : Traitement simple des chaînes de caractère en python mais il existe aussi si ‘s’ est une chaine de caractère la fonction booléenne ‘s.startswith(“….”)'.
Comprendre et créer un serveur simple
- serveur.py
# Définition d'un serveur simple # Le serveur attend la connexion d'un client et fait un echo des messages reçu import socket, sys #Adresse ip et port de la socket sur laquelle va ecouter le serveur # A ADAPTER A VOTRE MACHINE, vous pouvez mettre 0.0.0.0 pour écouter sur toutes les adresses IP du serveur # quelle est la conséquence d'écouter sur 127.0.0.1 ? HOST = '127.0.0.1' PORT = 2003 # 1) Il faut créer la socket en indiquant les protocoles (ici IP et TCP) mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2) puis effectuer la liaison (bind) try: mySocket.bind((HOST, PORT)) except socket.error: print("La liaison du socket à l'adresse choisie a échoué.") sys.exit() # 3) Un serveur est souvent une boucle infinie qui attend des connexions. while 1: # 4) Le serveur se bloque en attente de la requête de connexion d'un client print("serveur simple en attente...") mySocket.listen(5) # 5) Il est débloqué lors de l'établissement de la connexion connexion, adresse = mySocket.accept() print("Un client est connecté depuis l'adresse IP %s et le port %s" % (adresse[0], adresse[1])) # 6) Envoi d'un message de bienvenue au client # Attentionn la chaine de caractère DOIT etre convertie en un tableau d'octets # le paramète 'UTF-8" indique l'encodage des caratère qui doit être utilisé. connexion.send(("Vous êtes connecté au serveur "+HOST+":"+str(PORT)+".\n").encode('UTF-8')) # Le serveur commence maintenant un echange avec le client connecté while 1: # envoi de la question au client connexion.send("Votre message ?\n".encode('UTF-8')) # attente de la reponse msgClient = connexion.recv(1024) # le message est converti d'une tableau d'octets en chaine de caractères msgClient = msgClient.decode("utf-8") #si la reponse est FIN ou un ligne vide, le dialogue d'arrête. if msgClient.upper().strip() == "FIN" or msgClient.strip() == "": break # traitement de la réponse # le serveur affiche sur sa console print("reçu du client>"+msgClient+"<") # et envoi un echo au client connexion.send(("ECHO : "+msgClient).encode('UTF-8')) # 7) Si l'on est sorti de la boucle il faut terminer connexion.send("Good Bye.".encode('UTF-8')) print("Connexion interrompue.") #Le serveur ferme la connexion connexion.close() ch = input("Attendre un autre client ? <R>ecommencer <T>erminer ? ") if ch.upper() =='T': break
Ecrire un programme client/serveur simple
D'autres exercices...
./rsswatch.py http://www.univ-tln.fr/backend-breves.php3 informatique bruno@univ-tln.fr
envoi un email à bruno@univ-tln.fr quand le mot “informatique” est trouvé dans un titre d’une nouvelle de la page http://www.univ-tln.fr/backend-breves.php3
—- dataentry page —- type : TP enseignement_tags : S52 technologies_tags : Socket, TCP, Python themes_tags : Réseaux