diff --git a/Secured/secured_transaction.c b/Secured/secured_transaction.c index f6352b4..47d7650 100644 --- a/Secured/secured_transaction.c +++ b/Secured/secured_transaction.c @@ -2,7 +2,7 @@ #include <stdlib.h> #include <unistd.h> #include <fcntl.h> -#include <time.h> +#include <time.h> #include <sys/wait.h> #include <string.h> diff --git a/SecuredThread/Makefile b/SecuredThread/Makefile new file mode 100644 index 0000000..507b3c9 --- /dev/null +++ b/SecuredThread/Makefile @@ -0,0 +1,13 @@ +# Nom de l'exécutable +TARGET = secured_transaction_threads + +# Fichier source +SRC = secured_transaction_threads.c + +# Règle par défaut : compilation et lien +all: + gcc $(SRC) -o $(TARGET) + +# Nettoyage des fichiers générés +clean: + rm -f $(TARGET) \ No newline at end of file diff --git a/SecuredThread/README.md b/SecuredThread/README.md new file mode 100644 index 0000000..b7cebc7 --- /dev/null +++ b/SecuredThread/README.md @@ -0,0 +1,137 @@ +# 🔒 Transactions Sécurisées avec Threads + +Ce programme simule des transactions bancaires concurrentes en utilisant des **threads**, une approche plus efficace et légère que les processus. Les threads exécutent des transactions entre comptes bancaires, tout en partageant les ressources de manière optimale grâce à des mécanismes de verrouillage qui garantissent l'intégrité des données. + +--- + +## 📋 Description du Programme + +Le programme utilise des threads pour gérer plusieurs transactions simultanées entre comptes bancaires stockés dans un fichier de base de données. Contrairement à une version basée sur des processus, cette approche utilise un espace mémoire partagé, réduisant les coûts de communication et augmentant la performance. + +### Fonctionnalités principales : +1. **Gestion multithreadée des transactions :** + Chaque thread effectue un certain nombre de transactions aléatoires entre les comptes bancaires. + +2. **Synchronisation avec Mutex :** + Des verrous sont utilisés pour garantir des accès sécurisés au fichier contenant les comptes, empêchant les corruptions de données. + +3. **Optimisation des ressources :** + Les threads partagent la mémoire et les ressources du processus parent, réduisant les coûts liés à la création et à la gestion des entités concurrentes. + +4. **Calcul du solde total :** + Le programme vérifie l'intégrité des données en calculant le solde total avant et après les transactions. + +--- + +## ⚙️ Compilation et Exécution + +### Compilation +Utilisez le fichier `Makefile` pour compiler le programme : +```bash +make +``` +Cela génère un exécutable nommé `secured_transaction_threads`. + +### Exécution +Exécutez le programme en spécifiant : +- Le nombre de threads à lancer. +- Le nombre de transactions par thread. + +Exemple : +```bash +./secured_transaction_threads 4 10 +``` +Ici, 4 threads seront créés, chacun effectuant 10 transactions. + +--- + +## 🛡️ Pourquoi utiliser des Threads ? + +### **1. Partage de Mémoire :** +- Les threads partagent l'espace mémoire du processus parent, ce qui permet un accès rapide et direct aux données. +- Dans cette application, tous les threads peuvent accéder et modifier les comptes bancaires sans mécanismes complexes de communication inter-processus. + +### **2. Performance Optimisée :** +- Les threads sont plus légers que les processus. La création d'un thread est moins coûteuse en temps et en ressources. +- La commutation de contexte entre threads est plus rapide que celle entre processus, ce qui améliore les performances globales. + +### **3. Synchronisation Facile :** +- Grâce aux primitives comme les **mutex**, les threads peuvent synchroniser leurs accès aux ressources partagées de manière efficace. +- Ici, un mutex est utilisé pour s'assurer qu'un seul thread à la fois modifie le fichier des comptes bancaires. + +### **4. Adapté au Parallélisme :** +- Les threads permettent de tirer parti des architectures multicœurs, ce qui accélère les transactions simultanées. + +--- + +## 🛑 Problèmes Résolus +### **1. Incohérences de Données :** +Les mécanismes de verrouillage garantissent que chaque transaction est exécutée de manière atomique. Cela empêche les conflits d'accès entre plusieurs threads. + +### **2. Solde Final Correct :** +Grâce à la synchronisation, le solde total des comptes reste cohérent avant et après les transactions. + +### **3. Gestion des Transactions :** +Chaque transaction est complètement exécutée avant qu'une autre ne puisse accéder aux mêmes données. + +--- + +## 📜 Exemple de Résultat + +### Avant l'exécution +Le fichier `accounts.db` contient : +```plaintext +... +Diana 7145 +Diego 8494 +Dominic 4487 +Dylan 1157 +Easton 429 +Eden 5737 +Edward 3671 +Elaina 4904 +... +``` + +### Compilation et exécution +On compile le programme avec : +```bash +make +``` +ou +```bash +gcc secured_transaction_threads.c -o secured_transaction_threads -pthread +``` +Puis, on exécute : +```bash +./secured_transaction_threads 4 10 +``` + +### Après l'exécution +Voici une sortie typique du programme : + +```plaintext +Solde total initial : 400000€ +... +Transaction : Alivia -> Kaiden : 14€ +Transaction : Arianna -> Barrett : 3€ +Transaction : Rosalie -> Connor : 1€ +Transaction : Sara -> Arthur : 190€ +Transaction : Jude -> Sadie : 41€ +... +Solde total final : 400000€ +Tout est terminé sans incohérences ! +``` + +--- + +## 🚀 Points Forts de cette Version + +1. **Sécurité des Données :** + Les mécanismes de verrouillage évitent toute corruption des données. + +2. **Efficacité :** + L'utilisation de threads optimise les performances par rapport à une version basée sur des processus. + +3. **Simplicité :** + Les threads partagent directement les ressources, éliminant le besoin de communication inter-processus. diff --git a/SecuredThread/secured_transaction_threads.c b/SecuredThread/secured_transaction_threads.c new file mode 100644 index 0000000..ade36e4 --- /dev/null +++ b/SecuredThread/secured_transaction_threads.c @@ -0,0 +1,224 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <string.h> +#include <pthread.h> + +#define ACCOUNT_SIZE 16 +#define MAX_NAME_LEN 10 +#define FILENAME "../Data/accounts.db" + +// Couleurs pour l'affichage +#define RESET "\033[0m" +#define GREEN "\033[32m" +#define RED "\033[31m" +#define PINK "\033[35m" +#define BLUE "\033[34m" + +typedef struct { + int transactions; // Nombre de transactions à effectuer + int account_count; // Nombre de comptes disponibles + int transactions_done; // Nombre de transactions effectuées + int transactions_failed; // Nombre de transactions annulées + int total_transfered; // Montant total transféré par le thread +} ThreadData; + +pthread_mutex_t file_mutex; // Mutex pour protéger l'accès au fichier + +void read_account(int fd, int index, char *name, int *balance) { + char buffer[ACCOUNT_SIZE]; + + // Se positionner à l'index du compte dans le fichier + lseek(fd, index * ACCOUNT_SIZE, SEEK_SET); + + // Lire l'enregistrement du compte + read(fd, buffer, ACCOUNT_SIZE); + + // Extraire le nom et le solde du compte + sscanf(buffer, "%7s %d", name, balance); +} + +void write_account(int fd, int index, const char *name, int balance) { + char buffer[ACCOUNT_SIZE]; + + // Formater la chaîne + snprintf(buffer, ACCOUNT_SIZE, "%-7s%8d", name, balance); + + // S'assurer que le buffer est exactement de taille ACCOUNT_SIZE (16) + for (int i = strlen(buffer); i < ACCOUNT_SIZE - 1; i++) { + buffer[i] = ' '; + } + buffer[ACCOUNT_SIZE - 1] = '\n'; // Assurer la terminaison avec un saut de ligne + + // Se positionner à l'index du compte dans le fichier + lseek(fd, index * ACCOUNT_SIZE, SEEK_SET); + + // Écrire l'enregistrement du compte + write(fd, buffer, ACCOUNT_SIZE); +} + +void perform_transaction(int fd, int account_count, ThreadData *data) { + int from_idx = rand() % account_count; + int to_idx = rand() % account_count; + + while (from_idx == to_idx) { + to_idx = rand() % account_count; + } + + char from_name[MAX_NAME_LEN], to_name[MAX_NAME_LEN]; + int from_balance, to_balance; + + pthread_mutex_lock(&file_mutex); // Verrouiller l'accès au fichier + + read_account(fd, from_idx, from_name, &from_balance); + read_account(fd, to_idx, to_name, &to_balance); + + int max_transfer = from_balance / 5; + if (max_transfer > 0) { + int transfer_amount = rand() % max_transfer + 1; + + from_balance -= transfer_amount; + to_balance += transfer_amount; + + write_account(fd, from_idx, from_name, from_balance); + write_account(fd, to_idx, to_name, to_balance); + + // Mise à jour des statistiques + data->transactions_done++; + data->total_transfered += transfer_amount; + + printf(GREEN "Transaction : %s -> %s : %d€\n" RESET, from_name, to_name, transfer_amount); + } else { + data->transactions_failed++; + printf(RED "Transaction annulée : Fonds insuffisants pour %s\n" RESET, from_name); + } + + pthread_mutex_unlock(&file_mutex); // Déverrouiller l'accès au fichier +} + +void *thread_transactions(void *arg) { + ThreadData *data = (ThreadData *)arg; + + int fd = open(FILENAME, O_RDWR); + if (fd < 0) { + perror("Erreur : Impossible d'ouvrir le fichier"); + pthread_exit(NULL); + } + + for (int i = 0; i < data->transactions; i++) { + perform_transaction(fd, data->account_count, data); + usleep(rand() % 100000); + } + + close(fd); + pthread_exit(NULL); +} + +int calculate_total_balance() { + int fd = open(FILENAME, O_RDONLY); + if (fd < 0) { + perror("Erreur : Impossible d'ouvrir le fichier"); + return -1; + } + + off_t file_size = lseek(fd, 0, SEEK_END); + int account_count = file_size / ACCOUNT_SIZE; + + int total_balance = 0; + char name[MAX_NAME_LEN]; + int balance; + + for (int i = 0; i < account_count; i++) { + read_account(fd, i, name, &balance); + total_balance += balance; + } + + close(fd); + return total_balance; +} + +void print_statistics(ThreadData *thread_data, int num_threads) { + // En-tête du tableau avec une meilleure mise en forme + printf("\n%s--- Statistiques détaillées ---%s\n", PINK, RESET); + printf("%-10s%-25s%-25s%-20s\n", "Thread", "Transactions Effectuées", "Transactions Annulées", "Total Transféré (€)"); + + // Affichage des statistiques de chaque thread + for (int i = 0; i < num_threads; i++) { + printf("%-10d%-25d%-25d%-20d\n", i+1, thread_data[i].transactions_done, thread_data[i].transactions_failed, thread_data[i].total_transfered); + } + + // Ligne de séparation + printf("%s----------------------------- %s\n", PINK, RESET); +} + + +int main(int argc, char *argv[]) { + if (argc != 3) { + fprintf(stderr, "Usage: %s <number_of_threads> <transactions_per_thread>\n", argv[0]); + return 1; + } + + int num_threads = atoi(argv[1]); + int transactions_per_thread = atoi(argv[2]); + + int initial_balance = calculate_total_balance(); + if (initial_balance < 0) { + return 1; + } + printf(PINK "Solde total initial : %10d€\n" RESET, initial_balance); + + int fd = open(FILENAME, O_RDONLY); + if (fd < 0) { + perror("Erreur : Impossible d'ouvrir le fichier"); + return 1; + } + off_t file_size = lseek(fd, 0, SEEK_END); + close(fd); + + int account_count = file_size / ACCOUNT_SIZE; + printf(BLUE "Nombre de comptes : %d\n" RESET, account_count); + + pthread_mutex_init(&file_mutex, NULL); + + pthread_t threads[num_threads]; + ThreadData thread_data[num_threads]; + + for (int i = 0; i < num_threads; i++) { + thread_data[i].transactions = transactions_per_thread; + thread_data[i].account_count = account_count; + thread_data[i].transactions_done = 0; + thread_data[i].transactions_failed = 0; + thread_data[i].total_transfered = 0; + + if (pthread_create(&threads[i], NULL, thread_transactions, &thread_data[i]) != 0) { + perror("Erreur : Impossible de créer le thread"); + return 1; + } + } + + for (int i = 0; i < num_threads; i++) { + pthread_join(threads[i], NULL); + } + + pthread_mutex_destroy(&file_mutex); + + int final_balance = calculate_total_balance(); + if (final_balance < 0) { + return 1; + } + printf(PINK "Solde total final : %10d€\n" RESET, final_balance); + + if (initial_balance != final_balance) { + printf(RED "Le solde final est différent du solde initial...\n" RESET); + } else { + printf(GREEN "Le solde final est identique au solde initial !\n" RESET); + } + + print_statistics(thread_data, num_threads); + + printf("Tous les threads sont terminés !\n"); + + return 0; +} diff --git a/Unsecured/unsecured_transaction.c b/Unsecured/unsecured_transaction.c index 293bdd7..c22fc51 100644 --- a/Unsecured/unsecured_transaction.c +++ b/Unsecured/unsecured_transaction.c @@ -10,6 +10,13 @@ #define MAX_NAME_LEN 10 #define FILENAME "../Data/accounts.db" +// Couleurs pour l'affichage +#define RESET "\033[0m" +#define GREEN "\033[32m" +#define RED "\033[31m" +#define PINK "\033[35m" +#define BLUE "\033[34m" + // Fonction pour lire un compte dans le fichier avec verrouillage void read_account(int fd, int index, char *name, int *balance) { char buffer[ACCOUNT_SIZE]; @@ -64,9 +71,9 @@ void perform_transaction(int fd, int account_count) { write_account(fd, from_idx, from_name, from_balance); write_account(fd, to_idx, to_name, to_balance); - printf("Transaction : %s -> %s : %d€\n", from_name, to_name, transfer_amount); + printf(GREEN "Transaction : %s -> %s : %d€\n" RESET, from_name, to_name, transfer_amount); } else { - printf("Transaction annulée : Fonds insufisants %s\n", from_name); + printf(RED "Transaction annulée : Fonds insufisants %s\n" RESET, from_name); } }