Home / Programmazione / Java / Invio di file tramite socket in Java
Mattepuffo

Invio di file tramite socket in Java

Invio di file tramite socket in Java

Uno dei punti forti di Java, si sa, è il netoworking con le Socket.

Java mette disposizione parecchie classi e metodi per usarle; qui vi spiego tre semplici classi da usare per inviare file tramite esse:

  • una si occuperà della parte server
  • una del client
  • una di gestire il multi-thread

In verità le classi server e multi-threading sono strettamente collegate, mentre la parte client può anche stare separata (alias in un altro progetto).

Cominciamo col vedere la classe predisposta al multi-threading:

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 MTManager implements Runnable {

private final String DIR_SAVE = "path-file";
private Socket socket;

public MTManager(Socket socket) {
this.socket = socket;
}

public void run() {
try {
System.out.println("presa in carico nuova connessione da " + socket);
ObjectInputStream oin = new ObjectInputStream(socket.getInputStream());
File inFile = (File) oin.readObject();
File saveFile = new File(DIR_SAVE + "/" + inFile.getName()); // attenti al path, io sto Linux dove il separatore è /, mentre su Windows è \
save(inFile, saveFile);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
try {
socket.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}

private void save(File in, File out) throws IOException {
System.out.println(" --ricezione file " + in.getName());
System.out.println(" --dimensione file " + in.length());
FileInputStream fis = new FileInputStream(in);
FileOutputStream fos = new FileOutputStream(out);
byte[] buf = new byte[1024];
int i = 0;
while ((i = fis.read(buf)) != -1) {
fos.write(buf, 0, i);
}
fis.close();
fos.close();
System.out.println(" --ricezione completata");
}
}

Questa classe serve per istanziare un nuovo server ad ogni connessione, sennò si dovrebbe aspettare la conclusione dell'operazione per cominciarne una nuova.

 

 

Come vedete implementa l'interfaccia Runnable che deve essere implementata da ogni classe che deve essere esguita in thread.

Questa interfaccia ha un solo metodo, run() che viene qua implementato per le seguenti operazioni:

  • intercetta il file in arrivo ObjectInputStream
  • esegue il cast da oggetto a File (File inFile)
  • salva il tutto richiamando la directory di salvataggio e il metodo save (più sotto)

Il metodo save invece:

  • apre uno stream per il file ain arrivo (FileInputStream)
  • apre uno stream per salvare il file (FileOutputStream)
  • salva il file leggendo riga per riga (ciclo while)

Delle tre classi questo è l'unica che non ha metodo main, in quanto è utile solo quando il server ha una richiesta.

A questo punto vediamo il server vero e proprio:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

private final int PORT = 4444;

public static void main(String[] args) {
Server s = new Server();
s.listen();
}

private void listen() {
try {
ServerSocket ss = new ServerSocket(PORT);
System.out.println("Sono sulla " + ss);
for (;;) {
Socket s = ss.accept();
System.out.println("Conessione da " + s);
new MTManager(s).run();
Thread.sleep(100);
}
} catch (InterruptedException ex) {
System.out.println(ex.getMessage());
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}

A parte il metodo main che non fa altro che istanziare la classe, Server ha un solo metodo: listen().

Questa classe istanzia un oggetto ServerSocket che altro non fa che aspettare una connessione su una porta prestabilita.

Questo viene fatto grazie al ciclo for infinito, al metodo accept e al richiamo della classe MTManager per creare un nuovo thread:

for (;;) {
Socket s = ss.accept();
System.out.println("Conessione da " + s);
new MTManager(s).run();
Thread.sleep(100);
}

In parole povere:

  • aspetta la connessione
  • quando la riceve la accetta e istanzia un nuvo thread
  • imposta un sleep per non sovracaricare il thread

Manca solo il client., che come abbiamo detto è nettamente separato dalle altre due classi, tanto che potrebbe anche non esserci ed essere usato all'interno di un altro programma (cosa che faccio io):

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 HOST = "62.94.208.157";
private final int PORT = 9977;

public static void main(String[] args) {
Client c = new Client();
c.sendFile();
}

private void sendFile() {
Socket socket = null;
File file = new File("path");
try {
socket = new Socket(HOST, PORT);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.reset();
oos.writeObject(file);
oos.flush();
oos.close();
} catch (UnknownHostException ex) {
System.out.println(ex.getMessage());
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}

Il metodo sendFile crea un nuovo oggetto File per indicare quale file deve essere trasferito, e una Socket per impostare la connessione: host è l'indirizzo del server e port è la stessa porta del server.

Il file viene poi imesso in output stream con l'oggetto ObjectOutputStream.

In fin dei conti niente di così difficoltoso.