2015-01-26 12 views
5

Ho una classe TcpServer che è responsabile, beh, si comporta come un server TCP. È possibile trovare la classe di seguito:È possibile che due diverse istanze di socket ascoltino la stessa porta TCP (Porta già in uso)

public class TcpServer { 
    private ServerSocket serverSocket; 
    private Socket socket; 
    private int locallyBoundPort; 

    public TcpServer() { 


    } 

    public TcpServer(int locallyBoundPort) { 
     try { 
      this.serverSocket = new ServerSocket(locallyBoundPort); 
      serverSocket.setReuseAddress(true); 
     } catch (IOException e) { 
      System.out.println("Error at binding to port TCP : " + locallyBoundPort + "...cause : " + e.getMessage()); 
     } 
     socket = null; 

    } 

    public void accept() { 
     try { 
      socket = serverSocket.accept(); 
      socket.setReuseAddress(true); 
     } catch (IOException e) { 
      System.out.println("Error at accept : " + locallyBoundPort); 
     } 
    } 



    public void send(Data data) throws IOException { 
     if(socket != null) { 

      ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); 
      out.writeObject(data); 

     } 
    } 

    public Data receive() throws ClassNotFoundException, IOException { 
     if(socket != null) { 

       ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); 
       return (Data) in.readObject(); 

     } else { 
      return null; 
     } 
    } 

    public boolean bind(int port) throws IOException { 
     try { 
      this.serverSocket = new ServerSocket(port); 
      this.locallyBoundPort = port; 
     } catch(IOException e) { 
      return false; 
     } 
     return true; 

    } 

    public void close() { 
     try { 
      serverSocket.close(); 
      socket.close(); 
     } catch (IOException e) { 
      OzumUtils.print("IOException in close, TcpServer"); 
     } 

    } 

    public int getLocallyBoundPort() { 
     return locallyBoundPort; 
    } 

    public Socket getSocket() { 
     return socket; 
    } 

    public ServerSocket getServerSocket() { 
     return serverSocket; 
    } 
} 

E ho un pezzo di codice che fa questo:

TcpServer tcpServer = new TcpServer(LocalPort); 
while(1) 
{ 
    tcpServer.accept(); 
    Thread thread = new Thread(new runnable(tcpServer)); 
    thread.start(); 
    tcpServer = new TcpServer(LocalPort); 
} 

Comunque io sono sempre una porta già in uso errore. Ho pensato che due diverse istanze di socket potevano ascoltare la stessa porta mentre il multiplexing consente due connessioni attraverso la stessa porta quando il connettore ha un diverso IP o porta? Cosa mi manca?

+0

No, non è possibile utilizzare una porta già in stato di ascolto. Tuttavia, qualsiasi numero di client può connettersi a questa stessa porta. – m0skit0

risposta

5

Non è possibile collegare due socket del server TCP alla stessa porta. reuseAddress è davvero per i socket client, e non funziona come credi che lo faccia ... e il modo in cui lo stai usando non farebbe assolutamente nulla (perché lo stai impostando dopo l'associazione).

Non è necessario associare due volte alla stessa porta. Basta rimuovere questa linea tcpServer = new TcpServer(LocalPort); dal fondo del tuo ciclo while, e sarai tutto pronto.

Il modo in cui funziona è quello di collegare una volta il socket del server e ascoltare la porta. Quando arriva una connessione, forchetta un socket client per comunicare con il client e il socket del server originale continua ad ascoltare ulteriori connessioni.

In pratica, è necessario rimuovere il membro socket (e qualsiasi altro stato) dal proprio TcpServer e rendere il metodo accept restituire il socket accettato. Quindi aggiungi il tuo runnable come parametro invece di TcpServer e usalo per servire la connessione client. Quindi continua a chiamare accept nel ciclo e biforcarsi i thread per le nuove connessioni nello stesso modo in cui lo sai, ma non ricreare il server ogni volta.

Oppure, in alternativa, rimuovere il socket server e la porta da TcpServer, crea la presa all'esterno del ciclo, quindi while(true) chiamata accept su di esso, creare un nuovo TcpServer con il socket client restituito, e utilizzarlo in un thread per elaborare il connessione.

Non dimenticare di chiudere le prese del client dopo aver finito con loro.

+0

Ciao. Grazie per la tua risposta dettagliata. Lascia che ti chieda un'altra cosa per chiarire. Stai dicendo che quando un socket TCP accetta una connessione, può accettare contemporaneamente altri connettori e continuare a comunicare con i connettori precedenti? Ohhh, mentre sto scrivendo questo, ho capito che questo è il motivo per cui un nuovo socket viene restituito quando ServerSocket accetta un connettore. Grazie ! –

1

No, non è possibile utilizzare una porta già in stato di ascolto. Tuttavia, qualsiasi numero di client può connettersi a questa stessa porta. Non è necessario riascoltare la porta, è sufficiente generare una nuova discussione per elaborare la connessione corrente e attendere una nuova. Per esempio, supponendo di avere una classe che implementa TcpConnectionHanlderRunnable e prende il Socket come parametro, il ciclo sarebbe simile

while (true) { //while(1) is not valid Java syntax 
    final Socket tcpSocket = tcpServer.accept(); // Get socket for incoming connection 
    final Thread thread = new Thread(new TcpConnectionHanlder(tcpSocket)); // Create a thread for this socket/client connection 
    thread.start(); // Launch the thread 
    // tcpServer = new TcpServer(LocalPort); <- not needed, port still listening. 
} 

Poi, nel tuo TcpConnectionHanlder esempio a gestire questo particolare cliente (presa).