Visualizzazione dei risultati da 1 a 6 su 6
  1. #1

    [Java] - Comunicazione client-server: verificare quale socket invia dati

    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.");
            }
        }
    
    
    codice:
    
    
    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

  2. #2
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Quote Originariamente inviata da gagostin Visualizza il messaggio
    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ì.

    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
    Non ho letto tutto il codice postato, non ho molto tempo ora, però tieni presente questo di seguito.

    Innanzitutto su una rete di computer ogni interfaccia di rete (cablata o wireless che sia) è identificata da un indirizzo IP (v4 e/o v6) che può essere assegnato staticamente o dinamicamente tramite DHCP. Ma di certo deve essere univoco all'interno della rete, altrimenti ci sono conflitti e problemi vari. Il 127.0.0.1 è il "loopback" e nessuno lo vede sulla rete. È solo ad uso interno della macchina.
    Quindi ogni interfaccia sulla rete ha un indirizzo che sicuramente non è il loopback.

    Quando la accept() ti fornisce un Socket, esso contiene di base 4 informazioni: l'indirizzo e porta locale (del server) e l'indirizzo e porta "remoti" (del client). E questi ultimi sono appunto fondamentali per individuare l'endpoint a cui è connesso il server!
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  3. #3
    Ciao, la tua risposta è chiara.
    Purtroppo però, come nell'osservazione 3, le mie difficoltà nascono dalla volontà di lavorare sullo stesso pc.
    Mi spiego meglio: dal mio pc faccio partire un server (uso l'indirizzo di loopback per comodità ma potrei inserire anche l'IP reale); poi faccio partire 2 client in successione (sempre sullo stesso pc del server).
    Il server, quindi, salva i 2 socket nella mappa, con valore null.
    Quando i client vanno a registrare le proprie info sulla mappa tramite il register(), come possono riconoscere qual è il socket che si riferisce a loro? Infatti, entrambi i socket salvati avranno lo stesso IP nella mappa (che sia di loopback o reale) perché provengono dallo stesso PC.
    Voglio assolutamente evitare di usare più pc per fare le prove perché, al di là del fatto che non li ho, mi rallenterebbe tantissimo.
    Grazie mille della tua risposta, spero tu o chiunque altro possiate aiutarmi

  4. #4
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Quote Originariamente inviata da gagostin Visualizza il messaggio
    le mie difficoltà nascono dalla volontà di lavorare sullo stesso pc.
    Mi spiego meglio: dal mio pc faccio partire un server (uso l'indirizzo di loopback per comodità ma potrei inserire anche l'IP reale); poi faccio partire 2 client in successione (sempre sullo stesso pc del server).
    Il server, quindi, salva i 2 socket nella mappa, con valore null.
    Ti ripeto che, in generale, a livello di socket sono indirizzo e porta a contraddistinguere un endpoint cioè uno dei lati del socket.

    Quote Originariamente inviata da gagostin Visualizza il messaggio
    Quando i client vanno a registrare le proprie info sulla mappa tramite il register(), come possono riconoscere qual è il socket che si riferisce a loro?
    Quindi in un metodo invocato tramite RMI vuoi conoscere il socket che RMI sta usando? A naso credo non sia possibile. Ma non sono "esperto" su RMI, quindi preferisco non sparare scemenze.
    Il fatto è che RMI nasconde tutto ciò che sta "sotto", ovvero tutti i dettagli di basso livello relativi alla comunicazione tra client e server. So che c'è la possibilità di definire delle "factory" per poter creare Socket/ServerSocket speciali (es. con cifratura) ma questo di per sé non aiuta a capire "chi" ha fatto la chiamata.
    So che RemoteServer ha getClientHost() statico, che da dentro un metodo invocato con RMI ti permette di sapere il host del client. In questo momento non saprei dire se comprende la porta o no ... a naso credo di no (la documentazione parla solo di "host" e basta)

    Domanda: perché devi fare questo mix di gestioni? Un conto è farsi un "protocollo" di comunicazione direttamente su socket ... un altro conto è usare RMI.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  5. #5
    Per quanto riguarda la tua domanda, supponi di dover implementare un sistema in cui tutti i client connessi a un server possano sfidarsi l'un l'altro (a qualcosa). In questa ipotesi, il server deve tener traccia della lista di client ad esso connessi e i vari client devono poterla leggere quando vogliono. Quindi ho pensato che tramite le librerie RMI ogni client potesse avere una copia del server su cui richiamare alcuni metodi che esso mette a disposizione, come ad esempio il metodo getList().
    La domanda nasce dalla volonta di creare una mappa completa con tutte le informazioni del client (a priori il server non può saperle finché il client non gliele comunica). Quindi la registrazione sulla mappa se la deve fare il client tramite RMI, ma deve trovare un modo per riconoscersi nella lista.
    Magari c'erano modi migliori per farlo, ma spulciando un po' la rete mi è sembrata una strada appropriata.
    Comunque ho trovato il metodo che cercavo:
    dal socket del client recupero la porta locale: client.getLocalPort()
    per ogni socket sul server verifico che socket.getPort() sia uguale alla localPort (questo nel metodo register(), che quindi come parametro avrà proprio la local port del client).
    Quindi hai ragione, l'unico modo è tramite IP e porte, ma non avevo ben compreso il concetto di porta locale. Grazie mille del tuo aiuto!

  6. #6
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Quote Originariamente inviata da gagostin Visualizza il messaggio
    Comunque ho trovato il metodo che cercavo:
    dal socket del client recupero la porta locale: client.getLocalPort()
    per ogni socket sul server verifico che socket.getPort() sia uguale alla localPort (questo nel metodo register(), che quindi come parametro avrà proprio la local port del client).
    Mi sembra, scusa se lo dico, un approccio un po' contorto. Se con RMI vuoi realizzare un sistema distribuito in cui un dato inviato da un client arriva agli altri client, si può certamente fare con solo RMI. Questo è in sostanza il concetto delle chat. Se cerchi in rete "java rmi chat" trovi sicuramente qualcosa esempi o indicazioni utili.
    Ma con RMI devi ragionare in termini di oggetti ed invocazioni di metodi ... non di socket e indirizzi/porte. Questo è tutto nascosto da RMI.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

Tag per questa discussione

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.