View Full Version : [JAVA] Server/Client multithread sincro utente
Andrew28
13-09-2013, 12:24
Ciao a tutti ragazzi, ho creato una semplice social network multithread e fin ad ora andava tutto bene perchè testavo il tutto con un solo client, testando più client però mi sono accorto che appena loggo il secondo utente è come se anche il primo venisse sostituito.
Penso sia dovuto al fatto che io istanzio una variabile user nel Server e al momento del login vi inserisco il nome dell'utente che ha richiamato la login, ma cosi facendo appunto viene sovrascritto questo valore sempre dall'ultimo utente loggato impedendomi di fatto di usare il social network per come è stato pensato cioè molti thread in contemporanea che fanno richieste al server.
Io ho fatto anche una map dove associo il numero di porta della socketserver(che ho controllato essere diverso per ogni client che si logga) e il nome utente inserito al momento del login(non c'è password per entrare si inserisce solo il nome) il punto è che ora non so come sfruttarla questa map per fare in modo che ogni thread lavori a se e non interferiscano l'uno con l'altro.
Sapete dirmi quale possibile errore di codifica o concettuale c'è dietro?
al momento del login vi inserisco il nome dell'utente che ha richiamato la login, ma cosi facendo appunto viene sovrascritto questo valore sempre dall'ultimo utente loggato
Sapete dirmi quale possibile errore di codifica o concettuale c'è dietro?
L'hai descritto tu stesso l'errore. Quando un client si connette non devi sovrascrivere nulla, ma aggiungere un elemento alla map. Quando si disconnette, lo togli. Questo se ti serve avere l'elenco dei client connessi, altrimenti basta che ogni thread sappia a quale socket è legato (e già ora immagino lo sappia).
Andrew28
13-09-2013, 13:28
Ma il problema è che io devo usare il nome utente per passarlo a varie funzioni per questo avevo creato una variabile in cui inserirlo al momento del login cosi poi la variabil potevo passarla, senza di quella non so come fare a passargli l'informazione.
Quindi credo che il punto sia come faccio a estrarre il nome utente dalla mappa facendogli capire che proprio quell'utente sta facendo quella data richiesta?
Non complicare la cosa. Ogni thread sta gestendo una connessione (questo ho capito dal primo messaggio e concettualmente è corretto). Ora, quando un client invia il nome utente, andrai a salvarlo in una variabile locale al thread, non in un'unica variabile per tutti. Così ogni thread sa su quale socket sta lavorando, con quale utente, ecc. Per questo, come vedi, la map non ti serve proprio.
Andrew28
13-09-2013, 15:58
Si avevi ragione ho fatto cosi e tutto funziona, ora però sto testando le altre funzionalità che ho implementato in particolare siccome deve fungere anche da gioco quiz questa social network, un utente ha la possibilità di chidere la risposta di una domanda ad un amico facendo una richiesta "ask" a cui manda il domandaId della domanda per cui chiede un suggerimento e il nome dell'amico. Un altra funzione invece chiamata "seeDomande" permette ad un utente di vedere le richieste di suggerimento ricevuto composte da:
nomeamico<spazio>domanda
Premetto che sto usando graphstream quindi ogni utente è un nodo e ogni richiesta è un attributo, quindi io ho scritto il codice pensando che all'ask debba far avere anche il nome dell'utente che sta facendo la domanda cosi nell'attributo richieste dell'amico venga salvato direttamente questo nome e al momento della visualizzazione lo veda tranquillamente.
Il problema ora è che il nome dell'utente che sta facendo l'ask non riesco a darlo al server perchè ServerThread.user(che è la variabile nella mia classe ServerThread che contiene il nome utente) non è statico.
Il problema ora è che il nome dell'utente che sta facendo l'ask non riesco a darlo al server perchè ServerThread.user(che è la variabile nella mia classe ServerThread che contiene il nome utente) non è statico.
E che c'entra? Genericamente parlando, il client invia il comando (richiesta o risposta che sia). Il server lo riceve e lo passa a chi di dovere insieme al nome utente.
Andrew28
13-09-2013, 16:36
E che c'entra? Genericamente parlando, il client invia il comando (richiesta o risposta che sia). Il server lo riceve e lo passa a chi di dovere insieme al nome utente.
Esatto dovrebbe passarlo a chi di dovere insieme al nome utente che però non so come prenderlo.
Perchè il nome utente non deve essere mandato insieme a questa particolare richiesta, solo la domandaID e il nome dell'amico vanno passati. Ma nel Server ho comunque bisogno di avere il nomeutente per inserirlo nell'attributo. Spero di essermi spiegato chiaramente.
EDIT
ti aggiungo il codice:
public synchronized static boolean ask(String domandaId, String nome)
{
Node n = masterofquiz.getNode(nome);
String quest = null;
for (int i=0; i<domandeCorrenti.size(); i++){
if(domandaId.equals(domandeCorrenti.get(i).getId())){
quest = domandeCorrenti.get(i).getq();
}
else{
return false;
}
}
if(n.getAttribute("richieste") == null)
{
String richiesta = user+" "+quest;
n.addAttribute("richieste", richiesta);
}
else if(n.hasArray("richieste"))
{
Object[] array = n.getAttribute("richieste");
ArrayList<String> richieste = toArrayList(array);
richieste.add(user+" "+quest);
n.setAttribute("richieste", richieste.toArray());
}
else
{
Object[] r = new Object[2];
r[0] = n.getAttribute("richieste");
r[1] = user+" "+quest;
n.setAttribute("richieste", r); //lo aggiungiamo
}
return true;
}
Dove vedi scritto "user" quella è la variabile che contiene il nome utente nel ServerThread e questo codice è nel server quindi mi dice che non è istanziata se la lascio cosi, e se scrivo al posto di user, ServerThread.user mi segnala errore dicendo:
Cannot make a static reference to the non-static field ServerThread.user
Esatto dovrebbe passarlo a chi di dovere insieme al nome utente che però non so come prenderlo.
Perchè il nome utente non deve essere mandato insieme a questa particolare richiesta, solo la domandaID e il nome dell'amico vanno passati.
Il thread conosce il nome utente. Se deve richiamare una funzione a cui serve, glielo passa, semplice. Nel tuo codice, alla funzione ask va aggiunto un parametro (il nome utente appunto).
Attenzione che tutto quel codice deve essere thread-safe.
Andrew28
13-09-2013, 16:59
Ok, pensavo ci fosse un altro modo che passarlo tramite parametro perchè nelle mie direttive vi è specificato che ask deve prendere come parametri solo domandaID e nomeamico.
Però per esempio io nel server ho una funzione che va in background che controlla ogni secondo le date di chiusura delle domande(ogni domanda viene estratta da un file di testo ogni due minuti con una data di scadenza di 3 e in caso le elimina e controlla inoltre che l'utente che ha risposto alla domanda abbia risposto correttamente e in caso gli assegna un punto.
In realtà più utenti possono rispondere alla stessa domanda correttamente e a tutti al momento della chiusura della domanda verrà assegnato un punto.
Per farti capire meglio ti metto il codice della funzione rispondi e della funzione che controlla in background.
Sono entrambe nel Server:
rispondi:
public synchronized static boolean rispondi(String domandaId, String risposta)
{
//System.out.println("ECCO:" + Client.idNome + " "+ user + " " + userId);
boolean var = false;
for (int i=0; i<domandeCorrenti.size(); i++){
if(domandaId.equals(domandeCorrenti.get(i).getId())){
if(risposta.equals(domandeCorrenti.get(i).getr())){
//if(userId.get(i).contains(user) && userId.get(i)))
var = true;
utenti.put(domandaId, true);
}
else{
var = true;
utenti.put(domandaId, false);
}
}
else{
var = false;
}
}
return var;
}
In background:
public static class MyThread2 extends java.util.TimerTask {
synchronized public void run() {
now = java.util.Calendar.getInstance().get(java.util.Calendar.MINUTE);
for (int i=0; i<domandeCorrenti.size(); i++){
if (domandeCorrenti.get(i).getData().compareTo(now)<=0){
if(utenti.containsKey(domandeCorrenti.get(i).getId()) && utenti.containsValue(true)){
Server.updatePunteggio(ServerThread.user);
domandeCorrenti.remove(i);
i--;
}
else{
domandeCorrenti.remove(i);
i--;
}
}
}
}
}
Come vedi io ho bisogno del parametro user per passarlo ad updatePunteggio che semplicemente estrae l'attributo punteggio dell'utente e lo incrementa di 1.
Ma messo cosi non ho modo di avere il nome dell'utente a cui va aggiornato il nome perchè ServerThread.user mi da l'errore che ti ho scritto prima.
Sbaglio forse qualcosa concettualemente e si può fare questo tipo di controllo e aggiornamento in un altro modo?
Ok, pensavo ci fosse un altro modo che passarlo tramite parametro perchè nelle mie direttive vi è specificato che ask deve prendere come parametri solo domandaID e nomeamico.
Nessuno ti vieta di tenere una lista di utenti connessi (o di connessioni, per essere più generici) esterna ai thread, può essere la map di cui parlavi, un semplice array o un'apposita classe. Saranno i vari thread ad aggiornare questa lista (direttamente o indirettamente), ad ogni modo non avrai il problema che hai esposto inizialmente perchè ogni utente/connessione sarà un item a sè, senza problemi di sovrascrittura in caso di connessioni multiple (cosa che accadeva quando usavi la stessa variabile). Ovvio che anche (e soprattutto) questa lista deve essere thread-safe.
Andrew28
13-09-2013, 18:50
Penso di aver anche qualche problema con la thread safety se con questo intendi la sincronizzazione ecc.
Nonostante abbia passato ore a guardarmi di tutto sull'argomento ancora non mi è molto chiaro come devo fare per essere sicuro di averla implementata bene..
Per farti un esempio io ho messo alcuni metodi, che pensavo potessero essere a rischio, con la dicitura public synchronized eccecc ma non so se questo basta a rendere il tutto sincronizzato correttamente.
Penso di aver anche qualche problema con la thread safety se con questo intendi la sincronizzazione ecc.
Nonostante abbia passato ore a guardarmi di tutto sull'argomento ancora non mi è molto chiaro come devo fare per essere sicuro di averla implementata bene..
Per farti un esempio io ho messo alcuni metodi, che pensavo potessero essere a rischio, con la dicitura public synchronized eccecc ma non so se questo basta a rendere il tutto sincronizzato correttamente.
Cerca ancora perchè in rete di materiale ne puoi trovare davvero tanto, a partire da Wikipedia: http://en.wikipedia.org/wiki/Thread_safety
Se poi avrai problemi specifici su questo argomento ti consiglio di aprire un nuovo thread.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.