Qualche tempo fa mi e’ capitato di riprendere questo discorso leggendo e rispondendo ad un post sul newsgroup it.comp.java cosi ho deciso di scrivere questa micro guida che spiega molto velocemente come fare a trasferire un file da un pc ad un altro con il semplice utilizzo delle socket.
Ci sono molti altri modi per fare questo tipo di operazione oltre a tutti gli accorgimenti relativi alla sicurezza che qui non trattero’, ma, per chi si trova in questa situazione per la prima volta, potrebbe essere una buona traccia per iniziare.
Livello:
Medio, Facile
Per chi:
Chiunque sia alle prime armi con l’utilizzo di thread e socket
Scenario:
Trasferire file via socket client/server
Iniziamo con lo sviluppo del server che rimarra’ in attesa di nuove connessioni e file da ricevere, prima di tutto creiamo l’oggetto che si occupa, per ogni connessione, di ricevere e salvare il file, questo oggetto implemeta l’interfaccia Runnable cosi da poter gestire con i thread l’invio simultaneo di piu’ file e di conseguenza potrli salvare contemporaneamente senza dover aspettare che il primo arrivato finisca
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.net.Socket; public class ReceiverManager implements Runnable { private final String SAVE_DIR = "/home/crx/uploads"; private Socket socket; public ReceiverManager(Socket socket) { this.socket = socket; } public void run() { try { System.out.println("presa in carico nuova connessione da " + socket); // intercetto il file in arrivo ObjectInputStream oin = new ObjectInputStream(socket.getInputStream()); // eseguo un cast dell' oggetto come file File inFile = (File) oin.readObject(); // imposto il nuovo file che dovro' salvare // prendendone il nome originale File saveFile = new File(SAVE_DIR + "/" + inFile.getName()); // salvo il file save(inFile, saveFile); } catch (Exception e) { e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { } } } /** * Esegue il salvattaggio * * @param in * @param out * @throws IOException */ private void save(File in, File out) throws IOException { System.out.println(" --ricezione file " + in.getName()); System.out.println(" --dimensione file " + in.length()); // apro uno stream sul file che e' stato inviato FileInputStream fis = new FileInputStream(in); // scrivo uno stram per il salvataccio del nuovo file FileOutputStream fos = new FileOutputStream(out); byte[] buf = new byte[1024]; int i = 0; // riga per riga leggo il file originale per // scriverlo nello stram del file destinazione while((i=fis.read(buf))!=-1) { fos.write(buf, 0, i); } // chiudo gli strams fis.close(); fos.close(); System.out.println(" --ricezione completata"); } }
Ora creiamo il server vero e proprio che rimane in attesa di connessioni e, nel caso in cui qualche client si collegasse, provvedera’ ad instanziare ed eseguire in un thread un nuovo ReceiverManager per ogni nuova connessione.
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Server { private final int SERVER_PORT = 9977; public static void main(String[] args) { Server s = new Server(); try { s.listen(); } catch (IOException e) { e.printStackTrace(); } } private void listen() throws IOException { ServerSocket ss = new ServerSocket(SERVER_PORT); System.out.println("Sono sulla " + ss); // ciclo infinito per accettare per sempre connessioni for (;;) { // prendo la connessione in ingresso Socket s = ss.accept(); System.out.println("Conessione da " + s); // creo ed eseguo il thread per questa connessione // cosi il ciclo continua e rimane in attesa di // nuove connessioni new ReceiverManager(s).run(); // faccio respirare un po il ciclo try { Thread.sleep(100); } catch (InterruptedException e) { } } } }
Perfetto, ora abbiamo il server pronto a ricevere e salvare file provenienti anche da piu’ connessioni contemporanee, non rimane che creare il client che si occupera dell’invio vero e proprio di un file.
import java.io.File; import java.io.IOException; import java.io.ObjectOutputStream; import java.net.Socket; import java.net.UnknownHostException; public class Client { private final String SERVER_HOST = "127.0.0.1"; private final int SERVER_PORT = 9977; public static void main(String[] args) { Client c = new Client(); try { c.sendFile(new File(args[0])); } catch (IOException e) { e.printStackTrace(); } } private void sendFile(File file) throws IOException { Socket socket = null; try { socket = new Socket(SERVER_HOST, SERVER_PORT); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); oos.reset(); oos.writeObject(file); oos.flush(); oos.close(); } }
Semplice vero?
Ora non ci resta che provare il tutto per verificare il corretto funzionamento, partiamo con l’eseguire il server:
$ java Server Sono sulla ServerSocket[addr=0.0.0.0/0.0.0.0,port=0,localport=9977]
e successivamente il client passando compe parametro il file che vogliamo inviare
java Client /home/crx/wireless.log
ed ecco sull’output del server cosa avremo:
$ java sc/Server Sono sulla ServerSocket[addr=0.0.0.0/0.0.0.0,port=0,localport=9977] Conessione da Socket[addr=/127.0.0.1,port=33113,localport=9977] presa in carico nuova connessione da Socket[addr=/127.0.0.1,port=33113,localport=9977] --ricezione file wireless.log --dimensione file 50223 --ricezione completata
Ora analizziamo velocemente alcuni degli oggetti principali utilizzati in questo esempio:
- Socket e’ il punto finale della comunicazione tra due pc
- ServerSocket e’ l’oggetto che rimane in attesa di nuove connessioni, le gestisce e quando possibile restituisce il risultato al client
- ObjectInputStream e’ un oggetto utilizzato per deserializzare oggetti precedentemente serializzati siano essi tipi primitivi o oggetti
- ObjectOutputStream al contrario di ObjectInputStream si occupa di serializzare oggetti e tipi primitivi, solo oggetti che implementano l’interfaccia java.io.Serializable possono essere utilizzati in questo oggettoUna volta scritto l’oggetto nel metodo writeObject() e’ necessario eseguire il metodo flush() per assicurarsi che possa poi essere letto correttamente, senza rimaner bloccato, dal “ricevente”
Al solito spero di essere stato d’aiuto.
Ciao e alla prossima,
Cristian.
2 Responses
sonenos
October 22nd, 2010 at 2:27 pm
1Grazie davvero per questa spiegazione, molto d’aiuto!
Avrei una domanda.
Se volessi trasferire il file over UDP come faccio a mettere il file in un
DatagramPacket?
Grazie ancora
Cristian
October 22nd, 2010 at 3:25 pm
2Se ho capito quello che vuoi fare credo che ti basti utilizzare l’oggetto DatagramPacket(byte[] buf, int length, InetAddress address, int port) e poi inviarlo con il sent della DatagramSocket,
il file lo leggi con un FileInputStream e lo usi per riempire il buffer
Spero di esserti stato di aiuto
Ciao,
crx
RSS feed for comments on this post · TrackBack URI
Leave a reply
You must be logged in to post a comment.
Categories
Archives
Links
Meta
Calendar