Pour build :
./gradlew build
Pour run l'application principale, qui propose de lancer les clients et serveurs de chaque exercice :
./gradlew run
Après avoir build le projet en utilisant ./gralew build, le client et le serveur peuvent également être lancés directement avec :
./gradlew runUDPHorlogeServer./gradlew runUDPHorlogeClient
Cette tâche, comme toutes les autres, a été définie dans build.gradle.kts.
Faire attention qu'il n'y pas d'autres applications utilisant le même port lancées en même temps. Celui-ci peut également être changé dans UDPHorlogeClient.java et UDPHorlogeServer.java en modiant les constantes respectives. Ces instructions sont valablres pour les autres exercices.
Output côté serveur :
Serveur UDP en attente sur le port 12345
Réponse envoyée au client : 2025-10-09 12:17:22
Ouput côté client :
Requête envoyée au serveur :
Requête reçue du serveur : 2025-10-09 12:17:22
Client fermé
Run :
./gradlew runNTPServer./gradlew runNTPClient
On utilise UDP ici pour sa rapidité et sa simplicité comparé à TCP. Etant donné le contexte temporel de la synchronisation, le plus rapide le mieux.
Diagramme :
sequenceDiagram
participant Client
participant Serveur
Client->>Serveur: Envoie T1 (heure courante)
Note right of Serveur: T'_1 = heure de réception
Serveur-->>Client: Renvoie T1, T'_1, T'_2 (heure d'envoi)
Note left of Client: T2 = heure de réception
Client->>Client: Calcule δ = (T2 - T1) - (T'_2 - T'_1)
Client->>Client: Calcule θ = (T'_1 + T'_2)/2 - (T1 + T2)/2
Client->>Client: Corrige l'horloge : heure_courante += θ
Output côté serveur :
Serveur NTP démarré sur le port 12346
Serveur NTP en attente de messages...
Appuyez sur Ctrl+C pour arrêter le serveur
Message NTP reçu du client /127.0.0.1:33858 - T1=1760004937390, T'1=1760004937401
Réponse NTP envoyée - T1=1760004937390, T'1=1760004937401, T'2=1760004937416
Ouput côté client :
Client NTP connecté au serveur localhost:12346
Commandes:
'sync' - Effectuer une synchronisation NTP
'quit' - Quitter
Envoi de la requête NTP - T1 = 1760004937390
Réponse NTP reçue - T1=1760004937390, T'1=1760004937401, T'2=1760004937416, T2=1760004937418
=== Résultats de synchronisation NTP ===
Délai de transmission (delta) = 13 ms
Écart entre les horloges (theta) = 4.500 ms
Heure locale avant correction : 1760004937422
Heure corrigée : 1760004937427
Run :
./gradlew runTCPTimeServer./gradlew runTCPTimeClient
Output côté serveur :
Serveur TCP en attente sur le port 12347
Heure envoyée au client 2025-10-09 12:32:29
Connexion fermée.
Ouput côté client :
Connecté au serveur localhost sur le port 12347
Heure reçue du serveur : 2025-10-09 12:32:29
Connexion fermée par le serveur
Run :
runTCPEnhancedTimeServerrunTCPEnhancedTimeClient
Output côté serveur :
Serveur d'horloge parlante TCP démarré sur le port 12348
Serveur en attente de connexions...
Commandes supportées: DATE, HOUR, FULL, CLOSE
Appuyez sur Ctrl+C pour arrêter le serveur
Connexion acceptée de /127.0.0.1:34806
Début de session avec /127.0.0.1:34806
Commande reçue de /127.0.0.1:34806: DATE
Envoyé à /127.0.0.1:34806: Date: 2025-10-09
Commande reçue de /127.0.0.1:34806: HOUR
Envoyé à /127.0.0.1:34806: Heure: 12:46:46
Commande reçue de /127.0.0.1:34806: FULL
Envoyé à /127.0.0.1:34806: Date et heure: 2025-10-09 12:46:48
Commande reçue de /127.0.0.1:34806: CLOSE
Fermeture de connexion demandée par /127.0.0.1:34806
Connexion fermée avec /127.0.0.1:34806
Ouput côté client :
Connecté au serveur localhost:12348
=== Horloge Parlante TCP ===
Commandes disponibles:
DATE - Affiche la date
HOUR - Affiche l'heure
FULL - Affiche date et heure
CLOSE - Ferme la connexion
Tapez votre commande:
> DATE
Date: 2025-10-09
Run :
runCapitalizationServerrunCapitalizationClient
Output côté serveur :
Serveur de capitalisation TCP démarré sur le port 12349
Serveur en attente de connexions...
Connexion acceptée de /127.0.0.1:38572
Reçu de /127.0.0.1:38572: test
Envoyé à /127.0.0.1:38572: TEST
Ouput côté client :
Serveur: localhost:12349
Commandes:
'connect' - Se connecter au serveur
'quit' - Quitter le client
> connect
Connecté au serveur localhost:12349
=== Serveur de Capitalisation ===
Envoyez vos lignes de texte, elles seront renvoyées en majuscules
Tapez 'QUIT' pour fermer la connexion
> test
Capitalisé: TEST
La class à cette structure :
classDiagram
class Jeu {
+char JOUEUR_X
+char JOUEUR_O
+char VIDE
-char[][] grille
-char joueurCourant
-boolean partieTerminee
-char gagnant
+Jeu()
+void initialiser()
+synchronized boolean coupValide(int ligne, int colonne)
+synchronized boolean jouerCoup(int ligne, int colonne, char joueur)
-boolean verifierVictoire(char joueur)
-boolean grillePleine()
+synchronized String getGrilleString()
+synchronized char getJoueurCourant()
+synchronized boolean isPartieTerminee()
+synchronized char getGagnant()
+synchronized char getCase(int ligne, int colonne)
}
Les états sont stocké dans une grille simplement sous forme de tableau 2D de char. Le test des coup valide est donc simple :
- Le coup est dans les limites de la grille
- La case est vide
sequenceDiagram
participant C1 as Client 1
participant S as Serveur
participant C2 as Client 2
Note over C1,C2: Connexion des joueurs
C1->>S: Connexion TCP
S->>C1: BIENVENUE<br/>ATTENTE
C2->>S: Connexion TCP
S->>C2: BIENVENUE
Note over S: Choix aléatoire<br/>qui joue X/O
S->>C1: DEBUT X
S->>C2: DEBUT O
S->>C1: GRILLE (vide)
S->>C2: GRILLE (vide)
Note over C1,C2: Partie en cours
loop Tour de jeu
S->>C1: VOTRE_TOUR
C1->>S: COUP ligne colonne
S->>C1: COUP_VALIDE
S->>C2: COUP_ADVERSAIRE ligne colonne
S->>C1: GRILLE (état mis à jour)
S->>C2: GRILLE (état mis à jour)
S->>C2: VOTRE_TOUR
C2->>S: COUP ligne colonne
S->>C2: COUP_VALIDE
S->>C1: COUP_ADVERSAIRE ligne colonne
S->>C1: GRILLE (état mis à jour)
S->>C2: GRILLE (état mis à jour)
end
Note over C1,C2: Fin de partie
S->>C1: VICTOIRE
S->>C2: DEFAITE
- BIENVENUE : Connexion acceptée
- ATTENTE : En attente du second joueur
- DEBUT X/O : Le symbole attribué est X ou O
- VOTRE_TOUR : C'est au joueur de jouer
- COUP_VALIDE : Coup accepté
- COUP_INVALIDE raison : Coup refusé avec raison (case occupée, hors limites, pas votre tour, partie terminée, format incorrect)
- COUP_ADVERSAIRE ligne colonne : L'adversaire a joué
- GRILLE état : État de la grille (9 caractères)
- VICTOIRE/DEFAITE/MATCH_NUL : Résultat final
- ADVERSAIRE_DECONNECTE : L'adversaire s'est déconnecté
- ADVERSAIRE_QUITTE : L'adversaire a abandonné
- COMMANDE_INCONNUE commande : Commande non reconnue
- COUP ligne colonne : Jouer à la position (0-2, 0-2)
- QUITTER : Abandonner
- PRET : Joueur prêt (optionnel)
classDiagram
class Thread {
<<abstract>>
}
class Joueur {
-Socket socket
-BufferedReader in
-PrintWriter out
-char symbole
-Jeu jeu
-Joueur adversaire
-boolean estPret
+Joueur(Socket socket, Jeu jeu)
+void run()
-void traiterMessage(String message)
-void traiterCoup(int ligne, int colonne)
-void gererFinPartie()
-String getGrilleEnLigne()
+void envoyerMessage(String message)
-void fermerConnexion()
+char getSymbole()
+void setSymbole(char symbole)
+void setAdversaire(Joueur adversaire)
+boolean isEstPret()
}
Thread <|-- Joueur : extends
# Terminal 1 - Premier joueur
$ nc localhost 12345
BIENVENUE
ATTENTE
# Terminal 2 - Second joueur
$ nc localhost 12345
BIENVENUE
...
classDiagram
class Serveur {
-static final int PORT = 12345
-ServerSocket serverSocket
-Jeu jeu
-Joueur joueur1
-Joueur joueur2
+Serveur() throws IOException
+static void main(String[] args)
+void demarrer()
+void arreter()
}
classDiagram
class Client {
-static final String SERVER_HOST = "localhost"
-static final int SERVER_PORT = 12345
-Socket socket
-BufferedReader in
-PrintWriter out
-char monSymbole
-char[][] grille
-boolean monTour
-boolean partieEnCours
+Client()
+static void main(String[] args)
-void initialiserGrille()
+void connecter() throws IOException
-void recevoirMessages()
-void lireCommandes()
-void traiterMessageServeur(String message)
-void mettreAJourGrille(String etat)
-void afficherGrille()
+void fermer()
}