ajout de tous les cours et TP préparés cet été

This commit is contained in:
2026-01-17 23:10:49 +01:00
parent ed9415bc81
commit 301cf5a98f
125 changed files with 21614 additions and 542 deletions

View File

@@ -0,0 +1,424 @@
"""
Corrigé du TP Mini-Chat en Python
"""
import socket
import threading
# =============================================================================
# PARTIE 1 : Découverte des sockets
# =============================================================================
def afficher_info_reseau():
"""Affiche les informations réseau de la machine."""
nom_machine = socket.gethostname()
adresse_ip = socket.gethostbyname(nom_machine)
print(f"Nom de la machine : {nom_machine}")
print(f"Adresse IP locale : {adresse_ip}")
def resoudre_dns(domaine):
"""
Résout un nom de domaine en adresse IP.
:param domaine: (str) Le nom de domaine à résoudre
:return: (str) L'adresse IP correspondante
"""
try:
adresse_ip = socket.gethostbyname(domaine)
return adresse_ip
except socket.gaierror:
return f"Erreur : impossible de résoudre {domaine}"
# =============================================================================
# PARTIE 2 : Serveur TCP simple
# =============================================================================
def creer_serveur_tcp(port):
"""
Crée un serveur TCP qui attend une connexion et affiche le message reçu.
:param port: (int) Le port d'écoute
"""
# Créer le socket TCP
serveur = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Permettre la réutilisation de l'adresse
serveur.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Lier à toutes les interfaces sur le port spécifié
serveur.bind(('', port))
# Écouter (max 1 connexion en attente)
serveur.listen(1)
print(f"Serveur en écoute sur le port {port}...")
# Accepter une connexion
connexion, adresse = serveur.accept()
print(f"Connexion acceptée de {adresse[0]}:{adresse[1]}")
# Recevoir le message
donnees = connexion.recv(1024)
message = donnees.decode('utf-8')
print(f"Message reçu : {message}")
# Fermer les connexions
connexion.close()
serveur.close()
def creer_client_tcp(adresse_serveur, port, message):
"""
Crée un client TCP qui envoie un message au serveur.
:param adresse_serveur: (str) L'adresse IP du serveur
:param port: (int) Le port du serveur
:param message: (str) Le message à envoyer
"""
# Créer le socket TCP
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Se connecter au serveur
client.connect((adresse_serveur, port))
print(f"Connecté à {adresse_serveur}:{port}")
# Envoyer le message
client.send(message.encode('utf-8'))
print(f"Message envoyé : {message}")
# Fermer la connexion
client.close()
# =============================================================================
# PARTIE 3 : Chat bidirectionnel
# =============================================================================
def serveur_chat(port):
"""
Serveur de chat interactif.
"""
serveur = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serveur.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serveur.bind(('', port))
serveur.listen(1)
print(f"Serveur de chat en attente sur le port {port}...")
connexion, adresse = serveur.accept()
print(f"Connexion de {adresse[0]}:{adresse[1]}")
print("Chat démarré ! (le client envoie 'quit' pour terminer)\n")
while True:
# Recevoir un message du client
donnees = connexion.recv(1024)
if not donnees:
break
message_client = donnees.decode('utf-8')
print(f"[Client] {message_client}")
# Vérifier si le client veut quitter
if message_client.lower() == "quit":
print("Le client a quitté.")
break
# Envoyer une réponse
reponse = input("[Serveur] Votre réponse : ")
connexion.send(reponse.encode('utf-8'))
connexion.close()
serveur.close()
def client_chat(adresse_serveur, port):
"""
Client de chat interactif.
"""
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((adresse_serveur, port))
print(f"Connecté au serveur {adresse_serveur}:{port}")
print("Tapez 'quit' pour quitter.\n")
while True:
# Envoyer un message
message = input("[Vous] ")
client.send(message.encode('utf-8'))
# Quitter si demandé
if message.lower() == "quit":
print("Déconnexion...")
break
# Recevoir la réponse du serveur
reponse = client.recv(1024).decode('utf-8')
print(f"[Serveur] {reponse}")
client.close()
# =============================================================================
# PARTIE 4 : Comparaison TCP vs UDP
# =============================================================================
def serveur_udp(port):
"""
Serveur UDP simple.
"""
serveur = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # DGRAM = UDP
serveur.bind(('', port))
print(f"Serveur UDP en écoute sur le port {port}...")
while True:
# recvfrom retourne les données ET l'adresse de l'expéditeur
donnees, adresse = serveur.recvfrom(1024)
message = donnees.decode('utf-8')
print(f"[{adresse[0]}:{adresse[1]}] {message}")
if message.lower() == "quit":
break
serveur.close()
print("Serveur UDP arrêté.")
def client_udp(adresse_serveur, port):
"""
Client UDP simple.
"""
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print(f"Client UDP prêt à envoyer vers {adresse_serveur}:{port}")
print("Tapez 'quit' pour quitter.\n")
while True:
message = input("Message : ")
# sendto envoie directement sans connexion préalable
client.sendto(message.encode('utf-8'), (adresse_serveur, port))
if message.lower() == "quit":
break
client.close()
print("Client UDP arrêté.")
# =============================================================================
# PARTIE 5 : Mini-serveur multi-clients (Bonus)
# =============================================================================
clients = []
clients_lock = threading.Lock()
def gerer_client(connexion, adresse):
"""Gère la communication avec un client."""
print(f"[+] Nouveau client : {adresse}")
with clients_lock:
clients.append(connexion)
# Envoyer un message de bienvenue
connexion.send("Bienvenue sur le chat ! Tapez 'quit' pour quitter.\n".encode('utf-8'))
while True:
try:
message = connexion.recv(1024).decode('utf-8')
if not message or message.lower() == "quit":
break
print(f"[{adresse[0]}:{adresse[1]}] {message}")
# Diffuser le message à tous les autres clients
with clients_lock:
for client in clients:
if client != connexion:
try:
client.send(f"[{adresse[0]}:{adresse[1]}] {message}".encode('utf-8'))
except:
pass
except:
break
with clients_lock:
if connexion in clients:
clients.remove(connexion)
connexion.close()
print(f"[-] Client déconnecté : {adresse}")
def serveur_multi_clients(port):
"""Serveur acceptant plusieurs clients."""
serveur = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serveur.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serveur.bind(('', port))
serveur.listen(5)
print(f"Serveur multi-clients sur le port {port}")
print("En attente de connexions... (Ctrl+C pour arrêter)\n")
try:
while True:
connexion, adresse = serveur.accept()
thread = threading.Thread(target=gerer_client, args=(connexion, adresse))
thread.daemon = True
thread.start()
except KeyboardInterrupt:
print("\nArrêt du serveur...")
finally:
serveur.close()
def client_multi(adresse_serveur, port, pseudo):
"""Client pour le serveur multi-clients avec réception asynchrone."""
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((adresse_serveur, port))
print(f"Connecté au serveur {adresse_serveur}:{port} en tant que {pseudo}")
def recevoir():
"""Thread de réception des messages."""
while True:
try:
message = client.recv(1024).decode('utf-8')
if message:
print(f"\r{message}\n[{pseudo}] ", end='')
except:
break
# Lancer le thread de réception
thread_reception = threading.Thread(target=recevoir)
thread_reception.daemon = True
thread_reception.start()
# Boucle d'envoi
while True:
message = input(f"[{pseudo}] ")
if message.lower() == "quit":
client.send("quit".encode('utf-8'))
break
client.send(f"{pseudo}: {message}".encode('utf-8'))
client.close()
# =============================================================================
# PARTIE 6 : Réponses aux questions de synthèse
# =============================================================================
"""
RÉPONSES AUX QUESTIONS DE SYNTHÈSE
1. Différence entre SOCK_STREAM (TCP) et SOCK_DGRAM (UDP) :
- SOCK_STREAM (TCP) : Communication orientée connexion, fiable, ordonnée.
Les données arrivent dans l'ordre, sans perte ni duplication.
- SOCK_DGRAM (UDP) : Communication sans connexion, non fiable.
Les datagrammes peuvent arriver dans le désordre, être perdus ou dupliqués.
2. Pourquoi bind() pour le serveur et connect() pour le client ?
- Le serveur utilise bind() pour s'attacher à une adresse/port spécifique
et attendre les connexions entrantes (il écoute).
- Le client utilise connect() pour initier activement une connexion
vers un serveur connu (il se connecte à quelque chose qui existe déjà).
3. Que se passe-t-il si deux programmes utilisent le même port ?
- Le second programme recevra une erreur "Address already in use".
- Un port ne peut être utilisé que par un seul processus à la fois
(sauf configuration spéciale comme SO_REUSEADDR).
4. Pourquoi 127.0.0.1 (localhost) pour les tests ?
- C'est l'adresse de loopback qui pointe vers la machine locale.
- Les paquets ne quittent jamais la machine, idéal pour les tests.
- Pas besoin de configuration réseau ni de pare-feu.
5. Quand préférer UDP à TCP pour un chat ?
- Pour un chat vocal/vidéo en temps réel : la latence est critique,
perdre quelques paquets est acceptable (on n'entend pas un mot).
- Pour un chat textuel : TCP est préférable car chaque caractère compte.
"""
# =============================================================================
# TABLEAU COMPARATIF TCP vs UDP
# =============================================================================
"""
| Critère | TCP | UDP |
|------------------------|--------------------------|-------------------------|
| Connexion préalable | Oui (connect/accept) | Non (envoi direct) |
| Fonction d'envoi | send() | sendto() |
| Garantie de livraison | Oui (retransmission) | Non |
| Ordre des messages | Garanti | Non garanti |
| Utilisation typique | Web, email, fichiers | Streaming, jeux, VoIP |
"""
# =============================================================================
# TESTS
# =============================================================================
if __name__ == "__main__":
import sys
print("=" * 60)
print("CORRIGÉ DU TP MINI-CHAT")
print("=" * 60)
print("\n--- Partie 1 : Découverte des sockets ---\n")
print("Test afficher_info_reseau():")
afficher_info_reseau()
print("\nTest resoudre_dns():")
print(f"www.google.fr -> {resoudre_dns('www.google.fr')}")
print(f"www.wikipedia.org -> {resoudre_dns('www.wikipedia.org')}")
print("\n" + "=" * 60)
print("Pour tester les parties 2 à 5, lancez le script avec un argument :")
print(" python Corrige_TP_MiniChat.py serveur_tcp")
print(" python Corrige_TP_MiniChat.py client_tcp")
print(" python Corrige_TP_MiniChat.py serveur_chat")
print(" python Corrige_TP_MiniChat.py client_chat")
print(" python Corrige_TP_MiniChat.py serveur_udp")
print(" python Corrige_TP_MiniChat.py client_udp")
print(" python Corrige_TP_MiniChat.py serveur_multi")
print(" python Corrige_TP_MiniChat.py client_multi <pseudo>")
print("=" * 60)
if len(sys.argv) > 1:
commande = sys.argv[1]
if commande == "serveur_tcp":
creer_serveur_tcp(12345)
elif commande == "client_tcp":
creer_client_tcp('127.0.0.1', 12345, "Bonjour serveur !")
elif commande == "serveur_chat":
serveur_chat(12345)
elif commande == "client_chat":
client_chat('127.0.0.1', 12345)
elif commande == "serveur_udp":
serveur_udp(12345)
elif commande == "client_udp":
client_udp('127.0.0.1', 12345)
elif commande == "serveur_multi":
serveur_multi_clients(12345)
elif commande == "client_multi":
pseudo = sys.argv[2] if len(sys.argv) > 2 else "Anonyme"
client_multi('127.0.0.1', 12345, pseudo)
else:
print(f"Commande inconnue : {commande}")