Ruggine è un'applicazione di messaggistica instantanea multipiattaforma scritta in Rust, focus su performance e monitoraggio delle risorse.
Questo documento presenta il Progetto 2.1 – Ruggine, un'applicazione di chat testuale, sviluppata per il corso di Programmazione di sistema. Il progetto consiste nella realizzazione un sistema di tipo client/server in grado di gestire lo scambio di messaggi testuali tra utenti.
- Simone Columbaro – s343584
- Alex Drapant – s349529
- Lorenzo Brasolin – s349526
- Elia Francesco Vigè – s339126
La documentazione prodotta è articolata in più sezioni, ciascuna finalizzata a descrivere il progetto da punti di vista differenti: dalla progettazione all'utilizzo.
L'applicazione consente di creare gruppi di utenti per la condivisione di messaggi. L'accesso alla chat è effettuato all'avvio del programma, tramite una richiesta di login al server: è necessario fornire username e password di un account già presente nel database (pre-popolato). L'ingresso nei gruppi, invece, avviene esclusivamente tramite invito. L'applicazione è progettata per funzionare sui principali sistemi operativi, Windows, macOS e Linux. Nella parte finale di questo documento sono riportate le prestazioni del sistema, incluse le misurazioni del tempo di CPU e della dimensione dell’eseguibile. Inoltre, il sistema genera file di log contenenti i dettagli sull’utilizzo della CPU, registrati ogni 2 minuti.
Il sistema è stato progettato per soddisfare i seguenti requisiti
- Gestione di conversazioni singole (Direct Message) e di gruppo.
- Comunicazione in tempo reale tramite protocollo WebSocket.
- Peristenza dello storico messaggi garantita da un database relazionale.
- Compatibilità cross-platform (testato su Windows10/11 e macOS 12+)
- Monitoraggio attivo delle risorse (CPU/RAM) con logging periodico
Sistema operativo
- Windows 10 o successivo.
- macOS 12 o successivo.
Connessione di rete
- Rete Wi-Fi generica.
Requisiti software
- Rust : ≥ 1.80+.
- Visual Studio Build Tools (C++ workload) su Windows mentre Xcode Command Line Tools su MacOS.
In questa sezione vedremo come installare e far partire correttamente l'applicazione
- Scaricare l'applicazione dalla Repository ufficiale
- Aprire il progetto in Visual Studio Code o in qualunque altra IDE.
- Il progetto è suddiviso in due cartelle principali:
client/→ cartella contenente Clientserver/→ cartella contenente la logica di gestione delle risorse
Per ottenere le massime prestazioni e un eseguibile di dimensioni ridotte, si raccomanda la compilazione in modalità release
- Avvio server: aprire il terminale e usare i seguenti comandi in bash:
cd server
cargo run --release- Avvio client: aprire un nuovo terminale per ogni client che si desidera connettere e usare i seguenti comandi in bash:
cd client
cargo run Nota: Al primo avvio, Cargo scaricherà e compilerà automaticamente tutte le dipendenze.
- Inserire una delle seguenti credenziali:
- username:
simone, password:password - username:
alex, password:password - username:
lorenzo, password:password - username:
elia, password:password
Il progetto è organizzato come un Rust Workspace suddiviso in quattro crate principali:
protocol/server/client/hw-monitoring/
Il sistema adotta un'architettura Client-Server asincrona basata su scambio di messaggi JSON su WebSocket.
+-----------------------+ +--------------------------+
| CLIENT | | SERVER |
| | | |
| +-----------------+ | | +--------------------+ |
| | Interfaccia | | Connessione | | Gestore Connessioni| |
| | Grafica | |<==================>| | (Ricezione Dati) | |
| +--------+--------+ | WebSocket | +---------+----------+ |
| ^ | | ^ |
| | | | | |
| +--------v--------+ | | +---------v----------+ |
| | Modulo di Rete | | | | Logica Applicativa | |
| | (Comunicazione) | | | | (Smistamento Msg) | |
| +-----------------+ | | +---------+----------+ |
| | | ^ |
+-----------------------+ | | |
| +---------v----------+ |
| | Gestore Dati | |
| | (Salvataggio) | |
| +---------+----------+ |
| | |
+------------+-------------+
|
+-------v------+
| Archivio |
| (Su DB) |
+--------------+
Il frontend è un'applicazione desktop sviluppata interamente in Rust utilizzando egui, un framework GUI nativo e immediato che offre un'esperienza utente fluida e reattiva.
L'applicazione client è strutturata attorno a uno state centralizzato (AppState) che gestisce tutti i dati dell'interfaccia:
- Autenticazione: Stato di login gestito tramite enum
AuthState - Messaggistica: HashMap separate per messaggi diretti (
messages) e di gruppo (group_messages) - Gruppi: Lista dei gruppi con membri e metadati (
groups) - Gestione UI: Stato dell'interfaccia (
selected_chat,input_message, etc)
- Schermata login con username e password
- Validazione input e gestione errori
- Transizione automatica all'interfaccia principale dopo il login
L'interfaccia è divisa in tre pannelli:
- Pannello Laterale (Sidebar):
- Lista utenti connessi per chat dirette
- Lista dei gruppi con nome e conteggio membri
- Pulsante per la creazione di nuovi gruppi
- Pannello centrale:
- Header con informazioni sulla chat (nome utente/gruppo, lista membri)
- Area messaggi con scroll
- Campo di input per nuovi messaggi
- Modale per creazione gruppo:
- Selezione multipla degli utenti da invitare
- Campo per il nome del gruppo
- Conferma/annullamento creazione gruppo
- Caricamento Lazy: i messaggi vengono richiesti solo quando l'utente apre una chat
- Cache locale: i messaggi caricati vengono salvati in memoria per evitare richieste duplicate
- Separazione logica: HashMap separate per messaggi diretti e di gruppo
- Timestamp: visualizzazione dell'ora di invio per ogni messaggio (facendo hover sul messaggio)
- Creazione: selezione degli utenti e assegnazioni di un nome
- Visualizzazione membri: Header espandibile
- Colori utente: ogni membro riceve un colore distintivo generato deterministicamente
- Uscita dal gruppo: Pulsante dedicato per uscire dal gruppo, con aggiornamento real-time agli altri membri
Per riassumere, questi sono i pattern di design adoperati per la gestione del client:
- State management: Stato centralizzato con lock (Mutex) per thread-safety
- Separation of concerns: Funzioni separate per rendering dei diversi componenti UI
- Event-Driven: Reazioni agli eventi utente e ai messaggi del server
Il backend è costruito sopra il runtime asincrono Tokio.
-main.rs: Punto di ingresso. Inizializza il thread di monitoraggio risorse, apre il pool di connessione al DB e si mette in ascolto sulla porta TCP configurata (default 3001). Per ogni nuova connessione, spawna un task tokio.
-server.rs: Gestisce il ciclo di vita della connessione. Utilizza canali mpsc per gestire la concorrenza tra la ricezione di messaggi dal socket e l'invio di messaggi verso il client.
Questa cartella contiene il punto di ingresso dell'applicazione e la logica di gestione delle connessioni e degli eventi.
-
main.rs: È il punto di ingresso (Entry Point) del backend e gestisce l'infrastruttura di rete. Le sue responsabilità principali sono:- Inizializzazione: Configura il runtime asincrono
tokio, apre la connessione al database (DbPool) e avvia il thread per il monitoraggio hardware. - Gestione TCP/WebSocket: Si mette in ascolto sulla porta configurata e accetta le connessioni in ingresso, eseguendo l'handshake WebSocket tramite
tokio_tungstenite. - Task Spawning: Per ogni client connesso, genera ("spawna") un task asincrono isolato. Questo task gestisce due loop concorrenti: uno per la lettura dei messaggi dal socket e uno per la scrittura verso il client, utilizzando canali
mpscper la comunicazione interna. - Routing Iniziale: Deserializza i messaggi JSON in ingresso e delega la loro elaborazione alla logica di business definita in
server.rs.
- Inizializzazione: Configura il runtime asincrono
-
server.rs: Contiene il cuore della "Business Logic" e la gestione dello stato condiviso. Le sue componenti chiave sono:ServerState: Una struttura dati (protetta daArc<Mutex<...>>nel main) che mantiene in memoria la mappa dei client connessi, le sessioni utente attive (utenti loggati) e la cache dei gruppi.process_command: Funzione dispatcher che riceve i comandi inviati dal client (enumClientToServer) e chiama il gestore appropriato (es. login, invio messaggio, creazione gruppo).- Gestori di Eventi: Metodi specifici (come
handle_login,handle_dm,handle_create_group) che coordinano l'interazione tra la memoria volatile (stato delle connessioni) e la memoria persistente (chiamate al modulo DB).
Questa cartella astrae la logica di persistenza dei dati. Contiene i moduli che espongono funzioni Rust per eseguire query SQL sul database SQLite, suddivisi per entità logica:
-
client.rs: Gestisce le operazioni relative agli account utente:- Autenticazione: verifica della validità della coppia username/password (con gestione dell'hashing).
- Risoluzione identità: conversione da Username a UUID e viceversa, necessaria per mappare i messaggi in arrivo (che contengono ID) ai nomi visualizzati nella GUI.
- Recupero dati: ottenimento della lista completa dei client o di singoli record utente.
-
groups.rs: Gestisce la struttura e la composizione dei gruppi. Include funzioni per:- Creazione: inserimento di nuovi gruppi e associazione iniziale dei membri.
- Gestione membri: aggiunta massiva di utenti a un gruppo e rimozione (leave) di un utente specifico.
- Query: recupero di tutti i gruppi a cui appartiene un utente e, viceversa, di tutti i membri di un gruppo specifico.
-
messages.rs: Si occupa dello storico delle conversazioni. Fornisce le API per:- Persistenza: salvataggio di nuovi messaggi (sia diretti che di gruppo) con timestamp.
- Recupero Cronologia DM: caricamento dei messaggi scambiati tra due specifici utenti, ordinati temporalmente.
- Recupero Cronologia Gruppi: caricamento di tutti i messaggi associati a un
group_id.
-
mod.rs: È il punto di ingresso del modulo database. Si occupa di:- Inizializzare la connessione (Connection Pool) verso il file
database1.sqlite. - Esporre pubblicamente i sottomoduli (
clients,groups,messages) al resto dell'applicazione server.
- Inizializzare la connessione (Connection Pool) verso il file
Questo file contiene il database ed è suddiviso nel seguente modo:
- La struttura della tabella "clients" include le colonne :
id_client,nome_utente,password,salt
La comunicazione avviene tramite serializzazione JSON(crate serde_json).
Sono definite due enum principali nel crate condiviso protocol:
ClientToServer: Comandi inviati dal client (es.Login,Dm,CreateGroup).ServerToClient: Risposte o eventi push dal server (esWelcome,Dm,Join,LoginSuccess).
Struttura:
- Crate separato hw-monitoring con dipendenze: sysinfo, chrono, tokio, fs2
- Struct CpuMonitor che traccia nome processo, path log, e System di sysinfo
Funzionamento:
- Inizializzazione (CpuMonitor::new): riceve il nome del processo a cui aggiunge il PID, definisce dove scrivere il log e crea l'oggetto che leggerà dati quali CPU e memoria dal sistema operativo, racchiuso in Arc < Mutex > per un async sicuro
- Monitoraggio (start_monitoring): funzione async che ogni 2 minuti fa refresh dei processi, legge il PID corrente che usa per ottenere la CPU usage e la memoria (fisica in RAM) del processo con quel PID
- Scrittura (write_to_log): crea la direcotry logs/ se non esiste e apre il file cpu_usage-log in modalità append. Utilizza un lock esclusivo (fs2::FileExt) per evitare race conditions, di modo che più processi possano scrive sullo stesso file
Integrazione in server e client:
- Dopo l'avvio del processo principale nel main.rs viene spawnato un task asincrono separato che monitora le risorse in background, questo per ogni nuova istanza di server o client avviata
Sono stati effettuati test di monitorggio utilizzando il modulo hw-monitoringintegrato.
Configurazione di test [AppleM2], Build --release.
| Stato del Client | Utilizzo CPU Medio | Utilizzo Memoria (RAM) |
|---|---|---|
| Idle (Background) | < 1.0% | ~60 MB |
| Ricezione Messaggi | 4% - 6.0% | ~65 MB |
| Stato del Server | Utilizzo CPU Medio | Utilizzo Memoria (RAM) |
|---|---|---|
| Idle (Background) | < 1.0% | ~5 MB |
| Ricezione Messaggi | < 1.0 % | ~5 MB |
L'uso di cargo build --releaserimuove i simboli di debug e applica ottimizzazioni LTO (Link Time Optimization),
riducendo drasticamente la dimensione.
| Componente | Windows (.exe) | macOS (Binary) |
|---|---|---|
| Client (GUI) | ~5.7 MB | ~6,2 MB |
| Server (CLI) | ~4.2 MB | ~4,7 MB |




