ajout de tous les cours et TP préparés cet été
This commit is contained in:
424
Réseau/Corrige_TP_MiniChat.py
Normal file
424
Réseau/Corrige_TP_MiniChat.py
Normal 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}")
|
||||
Reference in New Issue
Block a user