View Full Version : [java] server - client multithread
ciao!!
ho un bisogno disperato di aiuto.
Sto facendo un progetto dove mi chiede di fare un servizio client-server multithread e implementare diversi comandi tipo rcd..rdir..read..write..
Ho però un problema grossissimo.. il programma se fatto partire con un client e un server funziona ma se si connettono 2 client viene servito solo l' ultimo che si è connesso.. mentre il primo rimane in attesa che gli arrivino degli stream dal server. il server però invia stream solo all' ultimo client.
Penso che questo sia dovuto anche dal fatto che non riesco a distinguere i thread dei client...
scrivo parte del server se può esser d' aiuto..
public class ServerTcp extends Thread{
....//variabili globali
private static Socket client;
// Costruttore
public ServerTcp (Socket socket) {
this.client = socket;
}
public static void main (String[] args) {
...
try {
System.out.println ("Server in creazione...");
ServerSocket server = new ServerSocket(portNum);
operationFileStateCount = 0;
System.out.println("Server creato, in ascolto sulla porta " + server.getLocalPort());
(new FromKey()).start();
while(true) {
client = server.accept();
System.out.println ("Richiesta di connessione dall'indirizzo IP: " + client.getInetAddress());
(new ServerTcp(client)).start();
}
}
catch (IOException e) {
System.err.println(e.getMessage());
}
}
else
System.err.println("Errore nel caricamento dei file di servizio. Terminazione programma.");}
public void run() {
try {
while(true) {
......
Spero mi possiate dare una mano... grazie mille ciao ciao
Spero mi possiate dare una mano... Ciao. Quel campo private static Socket client; non va affatto bene, perché essendo static è "di classe" e quindi di dato ne avresti 1 solo condiviso da tutte le istanze di ServerTcp. Togli lo static. ;)
ho provato a togliere lo static ma mi da errore perchè cmq è in un ambiente static..
non-static variable client cannot be referenced from a static context
client = server.accept();
^
ho provato a togliere lo static ma mi da errore perchè cmq è in un ambiente static..
non-static variable client cannot be referenced from a static context
client = server.accept();
^E infatti non è così che devi impostare quella variabile!!! Quel Socket restituito dalla accept() lo devi mettere prima in una variabile locale (con un qualunque altro nome) e poi passarlo al costruttore di ServerTcp, il quale poi a sua volta assegnerà correttamente il Socket alla variabile di istanza (quindi non static).
P.S.: <qui> (http://andbin.altervista.org/product.php?id=socket-sample1) sul mio sito c'è un esempio completo di client-server multi-thread. ;)
Io ti consiglio di mettere il main in una classe apposta che messo lì ti fa casino..il Socket client che hai dichiarato come variabile della classe ServerTCP lo usi anche nel main per ricevere socket dalla ServerSocket.
come consigliato da andbin togli static dalla variabile client
Crea una classe Main ( o come vuoi chiamarla) e spostati dentro il metodo main.
e modifica il main
Socket c;
while(true) {
c = server.accept();
System.out.println ("Richiesta di connessione dall'indirizzo IP: " + c.getInetAddress());
(new ServerTcp(c)).start();
}
grazie mille!!il problema era proprio in questa variabile static
pongo altri ostacoli che non riesco a risolvere...
sempre nella classe ServerTcp
public class ServerTcp extends Thread{
....
public static void main (String[] args) {
...
try {
System.out.println ("Server in creazione...");
ServerSocket server = new ServerSocket(portNum);
System.out.println("Server creato, in ascolto sulla porta " + server.getLocalPort());
FromKey fromKey = new FromKey();
/fromKey.start();
....
private class FromKey extends Thread {
public void run() {
try {
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String comand = input.readLine();
while (!comand.equalsIgnoreCase("quit")) {
System.out.println("Comando errato. L'unico comando utilizzabile e' 'quit'.");
comand = input.readLine();
}
System.out.println("Chiusura in corso...");
addToLogFileList("Chiusura effettuata.");
saveFileStateFile();
saveLogFile();
System.exit(-1);
}
catch(IOException e) {
System.out.println(e.getMessage());
}
}
}
}
c' è questa classe FromKey che mi serve per il seguente motivo:
il server può solo stare in attesa di richieste dai client l unico comando che può scrivere è quit.
quindi si crea questo thread per permettere al server di scrivere quit e chiudere la connessione al lato server..
i client a questo punto non sono obbligati a disconnettersi perchè possono eseguire comandi senza il server.. però il mio problema è come dire al/ai client connesso/i che il server ha chiuso la connessione.. cosa mi consigliate??
In più questo da un errore in compilazione:
non-static variable this cannot be referenced from a static context
FromKey fromKey = new FromKey();
vi ringrazio gia da ora..
Ascolta, se te hai un server e hai un client che si connette a quel server, la comunicazione per rimanere attiva ha bisogno che sia il server che il client siano connessi l'un l'altro.
Se uno dei due viene a mancare la connessione cessa di esistere.E' molto semplice.
Quello che vuoi fare non si capisce, così come l'hai spiegato non ha senso.
Quindi ti faccio due domande:
1) cosa intendi per "chiudere la connessione lato server"?
2) cosa intendi per "i client non sono obbligati a disconnettersi"?
precisa intanto queste due cose e vediamo di cosa hai bisogno...
Per il problema di compilazione si riferisce al fatto che usi una variabile non statica(fromkey) in un contesto statico(main penso), quello che ti suggerisco è di liberarti dal main e quindi dalla sua staicità creando classi apposite.
Intanto puoi risolvere con : static FromKey fromKey = new FromKey();
grazie.. allora provo a fare una classe a parte per il main e vediam che succede..
provo a spiegarmi meglio..
la classe FromKey (che è una classe all' interno della classe Server) serve per creare un thread che abbia come unico scopo di terminare la connessione con il comando quit.
si è dovuto usare questa classe perchè è l unico comando che si può digitare dal server..
Il client anche se disconnesso dal server deve cmq rimanere attivo nel senso che si possono ancora eseguire comandi (ovviamente quelli che non necessitano della connessione con il server).
problemi:
_ errore in compilazione descritto prima (scarsamente risolto creando una classe a parte per il main)
_ i client come si accorgono che il server ha chiuso la connessione?come faccio a segnalare a tutti i client connessi che la connessione è stata interrotta?
Se il server vuole chiudere la connessione significa che la connessione è aperta, non puoi quindi semplicemente mandare un segnale ai client?
Nel vero senso della parola, invii un dato(che può essere una stringa, un byte qualunque cosa) che venga interpretato dai client come messaggio di chiusura del server.
Ora non ricordo bene ma credo che usando il protocollo tcp, se il server chiude venga sollevata un'eccezzione dal client, se ricordo bene allora puoi anche interpretare questa eccezzione.
Comunque la cosa più bellina da fare è mandare a tutti i client connessi un messaggio di chiusura connessione cosichè i client sanno come comportarsi.
private class FromKey extends Thread {
public void run() {
try {
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String comand = input.readLine();
while (!comand.equalsIgnoreCase("quit")) {
System.out.println("Comando errato. L'unico comando utilizzabile e' 'quit'.");
comand = input.readLine();
}
System.out.println("Chiusura in corso...");
------ INVII UN DATO AI CLIENT------
addToLogFileList("Chiusura effettuata.");
saveFileStateFile();
saveLogFile();
--- FAI LE COSE PER BENE----
--- chiudi la connessione ----
System.exit(-1);
}
catch(IOException e) {
System.out.println(e.getMessage());
}
}
forse è una domanda stupida e forse e non c' entra nulla ma per inviare il segnale o l eccezione ai client (thread) avevo pensato di usare l indirizzo ip dei client altrimenti non sò come potrei fare ad indirizzare a tutti i thread un segnale.. (o comunque inviare loro un dato)
grassie cmq
Tu avrai sicuramente tanti thread quanti sono i client connessi(lato server intendo) ognuno di questi thread tiene aperta una connessione con un client ed è quindi possibile comunicare con esso.
Ogni thread di questo tipo(fatti una classe se non l'hai già fatto) deve avere un metodo del tipo: chiudiConnessione() invocandolo esegui il protocollo di chiusura.
Devi tenere tutti i riferimenti a questi thread(salvali in un array ad esempio) e passarli alla classe FromKey, in questo modo quando in quella classe ricevi il comando "quit" invochi il metodo chiudiConnessione() di ogni clientThread(i thread di prima).
Il metodo chiudiConnessione è quelcosa del tipo:
public void chiudiCOnnessione(){
----codice per inviare il dato di chiusura----
outputStream.flush();
outputStream.close();
inputStream.close();
serverSocket.close();
}
ho provato-> static FromKey fromKey = new static FromKey();
ma da errore: illegal start of expression
:doh:
ho provato-> static FromKey fromKey = new static FromKey();
ma da errore: illegal start of expression
:doh:
static FromKey fromKey = new FromKey();
Per il resto hai risolto? Hai capito cosa intendo?
grazie mille ho seguito il consiglio dell' eccezione..
ne approfitto un altro pò se posso..
in breve.. devo implementare dei comandi dove mi serve che un file venga copiato da server a client per poi esser letto o modificato da quest' ultimo.
ho provato ad usare sia la classe BufferedInputStream che la classe DataInputStream ma con scarsi risultati nel senso che a volte mi trasferisce il file ma non tutto (quindi se è un immagine non viene visualizzata) oppure mi crea semplicemente un file vuoto.. ho provato in mille modi..
LATO SERVER:
...
..
BufferedOutputStream out = new BufferedOutputStream(client.getOutputStream());
FileInputStream in = new FileInputStream(clientFile.getAbsolutePath());
byte buffer[] = new byte[1];
System.out.println( "Invio file in corso..." );
int i;
while((i = in.read(buffer,0,buffer.length)) != -1){
out.write(buffer,0,i);
}
out.flush();
out.close();
System.out.println( "Invio completato." );
LATO CLIENT:
System.out.println("Ricezione file in corso..");
BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
FileOutputStream out = new FileOutputStream(lDir + fileName);
byte buffer[] = new byte[1];
int i;
while ((i = in.read(buffer,0,buffer.length)) != -1){
out.write(buffer,0,i);
}
out.flush();
out.close();
in.close();
System.out.println( "Ricezione completata!" );
se però chiudo il socket dopo aver trasferito il file allora il file viene creato correttamente (a volte )ma ovviamente mi serve che il socket rimanga aperto...
HELP!!grassie
misantrophia
15-09-2006, 10:10
Si anch'io incontrai lo stesso problema in un programma che feci tempo fa, ma non mi ricordo come risolsi la questione....
Comunque mi sa che il problema riguarda questo ciclo:
while((i = in.read(buffer,0,buffer.length)) != -1){
out.write(buffer,0,i);
}
Provo a vedere se trovo il mio vecchio programma e ti farò sapere..
TempestaT400
18-09-2006, 01:47
Laura.. secondo me ti complichi solamente la vita con un approccio di questo tipo al problema.!.
Se devi servire più client contemporaneamente ti serve una classe che fa da server, aspetta la connessione di qualche client e poi lo fa servire da un'altra classe (Thread naturalmente) così da poter rimanere in ascolto per le altre comunicazioni...
Poi, se hai ancora problemi sulla creazione ,,, contattami via msn.. ti mando il mio contatto via PM:!:
mport java.io.*;
import java.net.*;
public class MultiServer {
public static void main(String [] args)throws IOException{
ServerSocket server = new ServerSocket(1050);
System.out.println("EchoMultiServer: started");
try{
while(true){
Socket clientSocket = server.accept();
System.out.println("Connection Accepted: "+clientSocket);
try{
new ServerThread(clientSocket);
}catch(IOException ioe){
clientSocket.close();
}
}
}catch(IOException ioe){
System.err.println("Accept failed!");
System.exit(1);
}
System.out.println("EchoMultiServer: closing...");
server.close();
}
}
E la classe Thread che instanzia il multiserver ad ogni richiesta!
import java.io.*;
import java.net.*;
public class ServerThread extends Thread{
private static int counter = 0;
private int id = ++counter;
private BufferedReader in;
private PrintWriter out;
private Socket socket;
public ServerThread(Socket s)throws IOException{
socket = s;
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
start();
System.out.println("ServerThread "+id+" started");
System.out.println("Socket: "+s);
}
public void run(){
try{
while(true){
//Logicamente qui metti il codice che serve a te.!.
}
System.out.println("ServerThread "+id+" closing...");
}catch(IOException ioe){}
try{
socket.close();
}catch(IOException ioe){}
}
}
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.