S52 - TP3 - Les sockets en python

La correction est disponible en téléchargement.

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()').

Exercice 1. Tester et comprendre chaque ligne de l’exemple suivant.
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()
Exercice 2. En vous inspirant de l’exemple précédent écrire deux clients pour le protocole Time (RFC 868). Un serveur pour ce protocole écoute sur la machine 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.
Exercice 3. Ecrire un programme python qui obtient des informations système avec 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(“….”)'.
Exercice 4. Tester et comprendre chaque ligne de l’exemple de serveur suivant. Que se passe-t-il si un client est déjà connecté et que l’on tente un autre connection.
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
Exercice 5. Ecrire un jeu qui permet à un client de devenir un nombre aléatoire choisi par le serveur. Le client indique le nombre maximum au serveur. Le serveur demande un nombre au client et répond ‘plus’ ou ‘moins’. La connection est terminée lorsque le nombre est trouvé.
Exercice 6. Ecrire un client qui joue seul au jeu précédent.
Exercice 7. Ecrire un programme qui teste les ports ouverts sur une machine dont le nom est passé en paramètre.
Exercice 8. Ecrire un programme qui extrait les titres (entre les balises '<title> et </title>') des nouvelles du site web de l’université données sur la page : http://www.univ-tln.fr/backend-breves.php3 et qui envoi email (cf. https://docs.python.org/3/library/email-examples.html) à une adresse données en paramètre quand un mot est trouvé dans le titre.

./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