Introduzione
Questo aritcolo nasce da un problema che probabilemte sarà capitato anche a voi: trasferire un file attraverso internet. Quante volte vi sarà successo di chiaccherare con qualcuno su MSN, e di voler inviare o ricevere un file. Ora, MSN consente di trasferire file, ma ha un piccolo problema: è lento. Non so perchè, ma è lento. Quindi per file grandi vi sarete trovati di fronte a una scritta del tipo: "55 minuti rimanenti", o anche peggio. Cosa fare allora? La seconda cosa che in genere si tenta è di inviare il file via email. Ahimè, molte caselle di posta elettronica hanno ancora il limite di 10MB per gli allegati... In realtà ci sono molti modi per superare il problema, compreso trasferire il file usando skype (che è più veloce di MSN a trasferire i file), oppure caricare il file in uno spazio web e inviare il link all'altra persona.Comunque, è proprio una volta che mi sono trovato ad affrontare questo problema che ho provato la soluzione più nerd che potessi pensare: scrivere un programmino server e uno client fatti apposta per trasferire file, inviare il client tramite MSN all'altra persona, e aspettare che il server sul mio computer ricevesse il file!
Ora, dato che dovevo risolvere un problema nel minor tempo possibile, il programma è davvero minimalista, non ha nemmeno una interfaccia grafica. Infatti, non lo pubblico su questo sito come soluzione al problema del trasferimento file, ma con scopo didattico, in quanto guardando il codice sorgente si può vedere come fare un server multithreading e un client in meno di 100 righe di codice ognuno.
I socket
Essendo un programmatore C++, ci sono poche sorprese su che linguaggio ho usato per scrivere il programma. Per effettuare una connessione internet occorre usare i socket, ma la libreria standard del C++ non offre i socket. Questo non è un grande problema, in quanto esistono almeno due soluzioni:- Usare i socket del C. Ho immediatamente scartato questa soluzione per due motivi: primo perchè i socket in C non sono portabili, in Windows ad esempio esiste una libreria simile chiamata WinSocket, che è però abbastanza diversa da dover richiedere di scrivere due volte lo stesso codice, una per Linux, MacOS, e l'altra per Windows. Il secondo problema è lo stile troppo di basso livello di queste librerie, che portano a scrivere molto codice e a fare facilmete errori.
- Usare i socket delle Boost. Le boost, per chi non lo sapesse, sono delle collezioni di librerie multipiattaforma per C++ molto utili per svariati compiti che prima o poi i programmatori di trovano ad affrontare. Il vantaggio di questi socket è quello di essere delle classi che consentono una programmazione ad alto livello, e sono pure portabili sia su Windows che Linux e MacOS senza dover riscrivere pezzi di codice.
ip::tcp::iostream socket("www.webalice.it","80");
Con questa riga si dichiara un istanza della classe ip::tcp::iostream di nome socket, che si connette al sito indicato dal primo parametro, e alla porta indicata dal secondo. Come primo parametro si può usare anche un indirizzo IP, ad esempio "127.0.0.1". Il costruttore della classe esegue da solo tutte le operazioni necessarie alla connessione, e si può quindi iniziare a trasmettere e ricevere subito. Per un elenco dei metodi di questa classe si può consultare sia la documentazione delle boost, sia quella della classe iostream standard (ad esmpio qui).
Per fare un server occorre qualche riga di codice in più, consiglio di guardare i sorgenti allegati alla fine dell'articolo.
Il protocollo
Dato che si tratta di una implementazione minimale, non ho implementato nessun protocollo standard come ad esempio FTP, ma ho scelto la soluzione più semplice che mi veniva in mente. Innanzitutto una volta aperta la connessione vengono trasmessi 4 byte sempre uguali, detti "magic". Il server li usa per controllare che dall'altra parte del socket c'è davvero il client che si aspetta, e se questi byte non sono corretti chiude la connessione. Poi viene trasmesso un byte che rappresenta la lunghezza del nome del file, poi viene trasmesso il nome del file e infine il contenuto binario del file. Non viene fatta nessuna codifica quale ad esempio base64 per ottenere la massima velocità.Il codice
Il codice è disponibile qui:Socket.zip
Il file contiene una cartella bin con i file precompilati per Linux e Windows, e due cartelle Client e Server con i sorgenti. Oltre ai sorgenti sono presenti i file di progetto per Netbeans, l'IDE che sto usando attualmente, che dovrebbero funzionare senza problemi su un computer Linux, e un makefile per Windows.
Se volete usare un IDE diverso, non dovete dimenticarvi di linkare con le librerie boost. Invece di spiegare come fare per ogni IDE esistente, riporto qui i vari comandi da dare in una shell per compilare i due programmi. Da questi comandi si dovrebbe capire come configurare ogni IDE (il primo comando è per compilare, il secondo per linkare)
- Per Linux
g++ -s -o client main.o -lpthread /usr/lib/libboost_system.a /usr/lib/libboost_thread.a
- Per Windows
g++ -s -o Client.exe main.o -lwsock32 -lws2_32 -lboost_system -lboost_thread
Ottenere le librerie boost
Per compilare correttamente i programmi bisogna linkare con le librerie boost, ma per poter linkare con tali librerie occorre prima averle installate sul proprio computer, e qui c'è una brutta sorpresa: sul sito ufficiale http://www.boost.org/ le librerie sono disponibili solo come codice sorgente. Anche se compilarle è facile, è un'operazione che richiede molto tempo, in genere più di un ora (le librerie sono davvero complesse internamente).Fortunatamente sia per Linux che per windows c'è un alternativa:
- Per Linux: Se avete Ubuntu/Kunbuntu/Xubuntu Jaunty (9.04) o
superiore, basta un
sudo apt-get install libboost-dev libboost-thread-dev - Per Windows: Basta scaricare MinGW-distro da qui.
MinGW è il porting del compilatore GCC per Windows. MinGW-distro è una
versione di MinGW che contiene molte librerie precompilate utili per
programmare, comprese le boost. Non dimenticatevi di settare la
variabile d'ambiente PATH come spiegato nel link.