Salve,
sono nuovo di questo forum quindi spero di non commettere errori nella discussione.
Ho cercato un po' in rete e non ho trovato nulla che facesse al caso mio.
Sto elaborando una comunicazione client server in Java utilizzando Socket e ServerSocket.
Una volta inizializzato il server, questo si mette in attesa di nuovi client tramite il metodo accept() della classe ServerSocket, che ritorna un Socket; da codice provvedo ad inserire subito tale socket in una mappa di client presente sul server, con chiavi i vari socket che man mano vengono accettati e valori delle classi Info.java contenenti varie informazioni sui client.
Poiché all'inizio il Server non ha alcuna informazione sul client se non il socket con cui comunica, il primo inserimento nella mappa è map.put(Socket, null). Provvederò da client a riempire il campo valore.
Ora, sul client inizializzo un Socket("127.0.0.1", 13001), cioè con indirizzo di loopback e porta 13001.
Una volta inizializzati i canali di comunicazione, automaticamente il client si connette al server.
Supponiamo ora che il client prelevi una copia remota del server tramite le librerie RMI (stub) e che il server metta a disposizione un metodo register che permetta di scrivere sulla mappa le informazioni provenienti dal client.
Come può il client risalire al socket con cui è stato registrato sul server?
In sostanza pensavo che il metodo accept() di server socket riportasse sul server il socket creato sul client all'avvio, cioè con il new Socket("127.0.0.1", 13001), invece a quanto pare non è così.
Per non saper né leggere né scrivere, inserisco anche qualche porzione di codice, così capite qual è il problema.
Classe server.
codice:
public class Server implements Runnable, RemoteServices {
...
private Map<ClientNode, Info> map = new HashMap<ClientNode, Info>();
...
public void run() {
ServerSocket ss = null;
try {
ss = new ServerSocket();
while (true) {
if (!ss.isBound()) {
ss.bind(new InetSocketAddress(ipServer, port));
}
Socket client = ss.accept();
CommunicationChannels channels = new CommunicationChannels(new ObjectOutputStream(client.getOutputStream()), new ObjectInputStream(client.getInputStream()));
map.put(new ClientNode(client, channels), null);
}
} catch (IOException e) {
e.printStackTrace();
}
}
// RemoteServices implementa Remote e mette a disposizione register()
public void register(Info info) throws RemoteException {
// TODO
}
Classe client.
codice:
public class Client implements Runnable {...
...
@Override
public void run() {
Socket client = null;
try {
client = new Socket(ipServer, port);
out = new ObjectOutputStream(client.getOutputStream());
in = new ObjectInputStream(client.getInputStream());
Registry registry = LocateRegistry.getRegistry("127.0.0.1");
stub = (RemoteServices) registry.lookup("remoteObject");
Info info = new Info();
info.setID(getID());
info.setUsername("Giordano");
stub.register(info);
} catch (IOException e) {
System.err.println("Spiacente: il server ha terminato l'esecuzione.");
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}
}
Osservazioni:
1. Non ho inserito il codice di alcune classi perché l'ho ritenuto superfluo; ad esempio la classe Info è una serie di getter e setter di alcuni campi, la classe CommunicationChannels sono i canali di comunicazione del client, prelevati direttamente da socket, ecc..
2. Il server, dopo l'accept(), non istanzia alcun thread per comunicare col client perché la comunicazione deve venire dopo; se tuttavia ci fosse il modo di risolvere il mio problema con un thread di comunicazione troverei il modo di aggiustare il tutto.
3. La domanda è partita dalla necessità di far interagire 2 client con un server senza utilizzare più pc; di conseguenza tutti i client avranno l'IP di loopback e di conseguenza non posso usare l'indirizzo IP come discriminante tra un socket e l'altro, altrimenti avrei già risolto.
4. Il metodo register() è ovviamente incompleto anche nella signature; una volta capito come confrontare il socket del server e del client tramite un oggetto discriminante Serializable (come potrebbe essere ad esempio l'hashcode()) probabilmente lo inserirei tra gli argomenti del metodo, in modo da poter fare facilmente il confronto.
Per concludere, le main:
codice:
public static void main(String[] args) {
Server server = new Server("127.0.0.1", 13001);
RemoteServices stub;
try {
stub = (RemoteServices) UnicastRemoteObject.exportObject(server, 0);
Registry registry = LocateRegistry.getRegistry();
registry.bind("remoteObject", stub);
(new Thread(server)).start();
} catch (RemoteException e) {
System.err.println("Verificare l'apertura dei registri");
} catch (AlreadyBoundException e) {
System.err.println("Server già attivo. Controllare che i registri siano chiusi correttamente.");
}
}
public static void main(String[] args) { Client client = new Client("127.0.0.1", 13001);
new Thread(client).start();
}
Sperando di essere stato chiaro e che possiate aiutarmi, auguro buon lavoro a tutti!
Ciao