View Full Version : [JAVA] Stream e socket
Oceans11
11-12-2006, 17:00
Ho appena cominciato ad usare il package java.net per la connessione tra socket(client+server), ma ancora non mi sono chiare molte cose, tanto che adesso sto confondendo anche gli stream che pensavo di aver capito bene!!! :cry:
Per esempio ho questa classe:
(scusate gli eventuali errori..l'ho scritta così su due piedi)
import java.net.*;
import java.io.*;
public class prova {
public static void main(String...args) throws Exception {
ServerSocket server = new ServerSocket(12345);
boolean listening = true;
while (listening) {
Socket client = server.accept();
BufferedReader r = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter w = new PrintWriter(client.getOutputStream());
//...
// qui faccio le operazioni che mi servono
//...
r.close();
w.close();
client.close();
server.close();
}
}
}
per quanto ho capito, nel lato client in input ottengo così l'output del server e viceversa. Ora come posso "cambiare" i 2 stream in FileInputStream e FileOutputStream??in parole povere come faccio a passare file tra lato client e lato server e viceversa???
per quanto ho capito, nel lato client in input ottengo così l'output del server e viceversa. Ora come posso "cambiare" i 2 stream in FileInputStream e FileOutputStream??in parole povere come faccio a passare file tra lato client e lato server e viceversa???Hai appena realizzato (a meno di errori che non ho verificato) quello che viene chiamato "server iterativo" (a differenza di un "server concorrente" che usa i thread).
Veniamo alla tua domanda finale cioè come far passare un file tra client e server: semplice, devi gestirlo tu! Da un socket puoi solo ottenere un InputStream e OutputStream. Cosa scrivi e leggi devi stabilirlo tu, magari con un protocollo apposito.
A parte il fatto che dovresti stabilire come scegliere il file da trasferire ma in sostanza dovresti fare così: da un lato apri il file con FileInputStream, leggi a blocchi di byte e li mandi sull'OutputStream del socket. Dall'altra parte fai esattamente l'opposto. Leggi dall'InputStream del socket e scrivi su un FileOutputStream.
Ma ripeto ... è una cosa che devi fare tu.
Oceans11
11-12-2006, 17:33
confidavo in una tua risposta!
ora vado a casa ed applico.
grazie mille!!!
Oceans11
12-12-2006, 18:39
da un lato apri il file con FileInputStream, leggi a blocchi di byte e li mandi sull'OutputStream del socket. Dall'altra parte fai esattamente l'opposto. Leggi dall'InputStream del socket e scrivi su un FileOutputStream.
pensavo di aver capito...ma non ne sono tanto sicuro!
nel copiare i file dal server al client si interrompe la lettura/scrittura e attende la pressione del tasto "invio", quindi se apro il file scaricato (lato client, per intenderci, anche se dovrebbe essere lo stesso...) ogni 1024 byte (la grandezza del buffer) c'ho il carattere "quadrato!!" che dovrebbere corrispondere all'invio non correttamente codificato (sto spiegando proprio male!! :D )
posto il codice della parte interessata:
public final int PORT = 12345;
private ServerSocket server = new ServerSocket(PORT);
private Socket client;
{
// ...
boolean listening = true;
while (listening)
{
client = server.accept();
String fileOrigine = "c:\\origine.txt";
String fileDestinazione = "c:\\destinazione.txt";
download(fileOrigine, fileDestinazione);
client.close();
}
// ...
}
public void download(String origin, String destination) throws IOException
{
InputStream is = client.getInputStream();
OutputStream os = client.getOutputStream();
FileInputStream fis = new FileInputStream(origin);
FileOutputStream fos = new FileOutputStream(destination);
byte[] buffer = new byte[1024];
byte[] buffer2 = new byte[1024];
while(fis.read(buffer) != -1) {
os.write(buffer);
os.flush();
is.read(buffer2);
fos.write(buffer2);
fos.flush();
}
fos.close();
fis.close();
}
PS: non ho la possibilità per il momento di provare il programma da remoto... dici che potrebbe funzionare??
PPS: client e server stanno sullo stesso file...uso il telnet per connettermi al server ed inviare le stringhe di comando.
^TiGeRShArK^
12-12-2006, 20:04
mmm..
non ho capito ke intendevi fare ma cmq il tuo programma si comporta correttamente secondo quanto scritto nel codice...
Lui sta in attesa di dati sul socket e quando tu scrivi invio con telnet lo legge e lo inserisce nel buffer prima dei dati ke va a leggere nel file.
Io nel frattempo mi sono messo a fare due classettine ProvaClient e ProvaServer:
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ProvaServer {
public ProvaServer() {
try {
char[] buffer = new char[1024];
ServerSocket server = new ServerSocket(4123);
Socket socket = server.accept();
InputStreamReader isReader = new InputStreamReader(socket.getInputStream());
BufferedReader reader = new BufferedReader(isReader);
FileWriter fileWriter = new FileWriter("c:/myClasspath.mine");
while(reader.read(buffer) != -1) {
fileWriter.write(buffer);
fileWriter.flush();
}
fileWriter.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ProvaServer server = new ProvaServer();
}
}
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class ProvaClient {
public ProvaClient() {
try {
char[] buffer = new char[1024];
Socket socket = new Socket("127.0.0.1", 4123);
FileReader fileReader = new FileReader("c:/classpath.txt");
OutputStreamWriter osWriter = new OutputStreamWriter(socket.getOutputStream());
BufferedWriter writer = new BufferedWriter(osWriter);
while(fileReader.read(buffer) != -1) {
writer.write(buffer);
writer.flush();
}
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ProvaClient provaClient = new ProvaClient();
}
}
che dovrebbero fare quello ke presumo fosse nelle tue intenzioni.
Il problema però è ke ovviamente leggendo buffer di 1000 caratteri e non eseguendo nessun filtro il file destinazione sarà sempre un multiplo di 1000 byte.
Secondo me la cosa migliore da fare quando si tratta di scambiare file è di inviare prima la dimensione totale del file da scambiare e quindi salvare su file solo il numero di byte ke ti interessano.
Ora non mi vengono in mente altre soluzioni più raffinate, ma sicuramente questa è la + semplice ed efficace ke mi è passata x la testa :p
Oceans11
12-12-2006, 20:12
mmm..
non ho capito ke intendevi fare ma cmq il tuo programma si comporta correttamente secondo quanto scritto nel codice...
Lui sta in attesa di dati sul socket e quando tu scrivi invio con telnet lo legge e lo inserisce nel buffer prima dei dati ke va a leggere nel file.
un momento...non ho capito bene...devo copiare il file non premere invio!!!
mi stai forse dicendo che devo interrompere l'input del client quando faccio download e upload???
in effetti...pensandoci un pò...non può che essere questo il problema!!!
però come posso farlo???
in ogni caso, grazie per la pazienza!
Oceans11
12-12-2006, 20:37
Il problema però è ke ovviamente leggendo buffer di 1000 caratteri e non eseguendo nessun filtro il file destinazione sarà sempre un multiplo di 1000 byte.
altrimenti si potrebbe controllare se il numero di byte letti sia minore della grandezza del buffer ed in tal caso scrivere solo quelli realmente letti???
(nota: il metodo int read(byte[] b) di FileInputStream ritorna il numero totale di byte letti nel buffer)
^TiGeRShArK^
12-12-2006, 20:45
un momento...non ho capito bene...devo copiare il file non premere invio!!!
mi stai forse dicendo che devo interrompere l'input del client quando faccio download e upload???
in effetti...pensandoci un pò...non può che essere questo il problema!!!
però come posso farlo???
in ogni caso, grazie per la pazienza!
A quanto ho capito io tu fai partire il serversocket in ascolto .
Per sbloccarlo e fargli ricavare il socket telnet inviandogli il carattere "invio".
In questo modo il primo carattere del socket è il carattere invio.
Quindi ti metti a ciclare per leggere il file, ma potrebbe capitare (non ho capito se è quello ke succede a te) ke ad ogni lettura di 1024 byte la tua classe resta in attesa di un altro carattere (dipende se il 1° byte viene consumato subito oppure no...ora non mi va di pensarci :p).
In quel caso ti potresti ritrovare il carattere nel mezzo del file.
La soluzione migliore è ovviamente di utilizzare i thread, ma anche come ho fatto io, dividendo il programma in un client e un server tutto funziona ad hoc.
In questo modo infatti il server sta in attesa fin quando il tuo client non si connette e inizia a mandare dati.
Alla fine della trasmissione il server chiude la connessione (sempre se mi sono ricordato di aggiungere il socket.close()... ma ne dubito :asd: ).
Le mie due classettine però hanno il problema che scrivono nel file sempre un buffer di 1000 char e quindi anke se il file originario è di un solo byte, quello di destinazione avrà dimensione 1000.
Per ovviare a questo problema basta inviare come prima cosa la dimensione del file e quindi scrivere solo quel preciso numero di char (o di byte) nel file di destinazione.
Cmq una cosa che devi assolutamente fare è dividere le logiche del client e del server perchè sennò non ci capisci niente in quel modo con l'invio e la ricezione concatenate :D
Ecco un esempio semplice semplice. Nota che è certamente migliorabile, principalmente nella gestione degli errori. Ma l'ho scritto velocemente perché stasera vado di fretta! Domani con calma lo aggiorno magari.
Compila e poi lancia:
java FileServer 1234
e con un altro prompt
java FileDownloader localhost 1234
Quando ti mostra >>> metti un path completo C:\blabla di un file e vedrai che te lo scarica nella directory dove sei.
^TiGeRShArK^
12-12-2006, 20:56
altrimenti si potrebbe controllare se il numero di byte letti sia minore della grandezza del buffer ed in tal caso scrivere solo quelli realmente letti???
(nota: il metodo int read(byte[] b) di FileInputStream ritorna il numero totale di byte letti nel buffer)
si...
potresti anke fare in quel modo..
però..
non funziona :D
Ora non ricordo bene il motivo, ma se non sbaglio mi aveva dato qualke problemino...
O meglio.
In locale e su rete LAN non da alcun problema se non erro..
ma quando ho provato ad usarlo col cellulare mediante UMTS mi ha iniziato a sbroccare di brutto :asd:
Il problema se non erro era il carattere di chiusura del file..
nel senso che con le latenze dell'umts mi capitava che il socket non riceveva niente e qdi quando andavo a leggere mi ritrovavo un bel -1.
Quindi l'ideale è vedere quant'è la dimensione totale del file e leggere fino a quel numero di byte.
In questo modo avrai la sicurezza di aver ricevuto tutti i tuoi dati ;)
Oceans11
13-12-2006, 14:03
A quanto ho capito io tu fai partire il serversocket in ascolto .
Per sbloccarlo e fargli ricavare il socket telnet inviandogli il carattere "invio".
In questo modo il primo carattere del socket è il carattere invio.
Quindi ti metti a ciclare per leggere il file, ma potrebbe capitare (non ho capito se è quello ke succede a te) ke ad ogni lettura di 1024 byte la tua classe resta in attesa di un altro carattere (dipende se il 1° byte viene consumato subito oppure no...ora non mi va di pensarci :p).
In quel caso ti potresti ritrovare il carattere nel mezzo del file.
La soluzione migliore è ovviamente di utilizzare i thread, ma anche come ho fatto io, dividendo il programma in un client e un server tutto funziona ad hoc.
In questo modo infatti il server sta in attesa fin quando il tuo client non si connette e inizia a mandare dati.
stanotte ho provato a farlo funzionare ma niente...il più delle volte ottengo file più o meno grandi e più o meno riempiti con il "quadratino"!!!
ora comunque provo le vostre classi e vediamo cosa salta fuori...tornerò (quasi sicuramente) a chiedere ulteriori consigli!!
Cmq una cosa che devi assolutamente fare è dividere le logiche del client e del server perchè sennò non ci capisci niente in quel modo con l'invio e la ricezione concatenate :D
in effetti mi sono proprio ingarbugliato!!! :rolleyes: è meglio che scrivo le due classi separate!!!
grazie per le risposte.
Oceans11
13-12-2006, 15:03
Ecco un esempio semplice semplice. Nota che è certamente migliorabile, principalmente nella gestione degli errori. Ma l'ho scritto velocemente perché stasera vado di fretta! Domani con calma lo aggiorno magari.
Compila e poi lancia:
java FileServer 1234
e con un altro prompt
java FileDownloader localhost 1234
Quando ti mostra >>> metti un path completo C:\blabla di un file e vedrai che te lo scarica nella directory dove sei.
uff...ma a te funziona??a me si inchiodava FileDownloader appena entrato nel while...dopo aver stampato il nome del file che gli davo...poi ho pensato che forse il printwriter aveva bisogno di un flush...ora và...ma perchè mi assillano così tanto sti stream??e faccio girare il programma in locale ancora!!!non oso pensare cosa può succedere su una rete lenta... :cry:
comunque grazie per il vostro tempo!
Oceans11
13-12-2006, 17:36
Se metto insieme in una stessa classe il codice del client e del server succede che il file non lo copia ma stampa soltanto gli "invio" (i quadratini, ricordate?) che gli dò quando interrompe la lettura...ora non voorei dire fesserie ma credo che sia colpa del fatto che l'input stream del socket lo uso per:
1) colloquiare tra la classe (client+server) ed il telnet (client vero e proprio)
2) per leggere lo stream del socket dopo che c'ho scritto su una parte del file!
come risolvo mantenendo sempre una sola classe??
^TiGeRShArK^
13-12-2006, 18:20
Se metto insieme in una stessa classe il codice del client e del server succede che il file non lo copia ma stampa soltanto gli "invio" (i quadratini, ricordate?) che gli dò quando interrompe la lettura...ora non voorei dire fesserie ma credo che sia colpa del fatto che l'input stream del socket lo uso per:
1) colloquiare tra la classe (client+server) ed il telnet (client vero e proprio)
2) per leggere lo stream del socket dopo che c'ho scritto su una parte del file!
come risolvo mantenendo sempre una sola classe??
...usa i thread...
lanci nel metodo run il server e nel flusso normale esegui il client..
ma è davvero sporchissima come soluzione :Puke:
Perchè vuoi x forza usare una sola classe? :mbe:
non ha alcun senso...
anke xkè concettualmente Client e Server dovrebbero stare su macchine diverse :D
Oceans11
13-12-2006, 19:24
...usa i thread...
lanci nel metodo run il server e nel flusso normale esegui il client..
ma è davvero sporchissima come soluzione :Puke:
Perchè vuoi x forza usare una sola classe? :mbe:
non ha alcun senso...
anke xkè concettualmente Client e Server dovrebbero stare su macchine diverse :D
ho capito!
Non riesco ad immaginarmi proprio bene il programma, ho tanta confusione in testa!! :muro:
in effetti il vero client dovrebbe essere il telnet che si connette al server(ServerSocket)...però ho bisogno anche del client (Socket) che a quanto ho capito lo ottengo solo dall'accept() del server...inoltre da lato client vorrei avere solo il telnet e non una classe...!!
hai suggerimenti?
^TiGeRShArK^
13-12-2006, 20:20
ho capito!
Non riesco ad immaginarmi proprio bene il programma, ho tanta confusione in testa!! :muro:
in effetti il vero client dovrebbe essere il telnet che si connette al server(ServerSocket)...però ho bisogno anche del client (Socket) che a quanto ho capito lo ottengo solo dall'accept() del server...inoltre da lato client vorrei avere solo il telnet e non una classe...!!
hai suggerimenti?
ehm..
mi sono perso :D
ke te ne fai di telnet? :p
Se lo usi per inviare comandi devi solo avere un server senza client ke accetti i comandi li parsifiki e faccia le operazioni ke vuoi..
ma senza una classe client ad esempio non potrai ricevere alcunkè dal server..
ma potrai solo eseguire appunto dei comandi da remoto...
Se mi spieghi x bene cosa vuoi fare magari riesco anke ad esserti d'aiuto :asd:
Oceans11
14-12-2006, 10:34
mmm...sto alquanto fulminato eh?? :doh:
ma non è sempre così giuro!!!
allora ti dico cosa ho in mente così magari ti riesci a districare meglio nei miei pensieri!!! :D
il server (o meglio lo chiamo programma A perchè così non sbaglio...) dovrebbe poter:
1) interpretare ed eseguire comandi inviati con il telnet(es: upload e download files; in generale i più importanti comandi del prompt..)
2) accettare connessioni multiple (non necessario)
3) redirezionare tutto l'output del server (tutto, anche gli errori) al client, magari su un file di log!
assomiglia molto a una connessione di assistenza remota :D !?!?!
[edit] in effetti...pensandoci un pò a mente lucida (durerà poco la mia lucidità) posso benissimo lasciar stare il telnet e fare tutto con un client...giusto?
Oceans11
16-12-2006, 11:57
[edit] in effetti...pensandoci un pò a mente lucida (durerà poco la mia lucidità) posso benissimo lasciar stare il telnet e fare tutto con un client...giusto?
l'unico problema è che così facendo devo portarmi a spasso e copiare sui computer che uso oltre che il server anche il client...mentre con telnet....beh ci sta già no? :D
mentre con telnet....beh ci sta già no? :DSì ... ma il telnet non è adatto per quello che vuoi fare tu. Tutto ciò che il server invia al telnet, è visibile solo a video. In pratica .... è solo un "terminale" e basta.
Oceans11
16-12-2006, 12:15
hai ragione e avevo capito...
quindi devo scegliere:
1) telnet come client ma niente download file dal server...
2) classe client con download file dal server ma no telnet...giusto?
1) telnet come client ma niente download file dal server...
2) classe client con download file dal server ma no telnet...giusto?Esatto. Scegli la soluzione 2)
Oceans11
16-12-2006, 12:35
ok ci provo...in ogni caso torno a chiedere consigli :D
grazie mille!
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.