Come posso interrompere un metodo Accept () ServerSocket?


143

Nel mio thread principale ho un while(listening)loop che chiama accept()il mio oggetto ServerSocket, quindi avvia un nuovo thread client e lo aggiunge a una raccolta quando viene accettato un nuovo client.

Ho anche un thread Admin che voglio usare per emettere comandi, come 'exit', che farà chiudere tutti i thread client, spegnersi e chiudere il thread principale, trasformando l'ascolto in false.

Tuttavia, la accept()chiamata nel while(listening)loop si blocca e non sembra esserci alcun modo per interromperla, quindi la condizione while non può essere ricontrollata e il programma non può uscire!

C'è un modo migliore per farlo? O un modo per interrompere il metodo di blocco?



Per il pepole che vuole aspettare x tempo e quindi reagire usa setSoTimeout ().
Abdullah Orabi,

Risposte:


151

Puoi chiamare close()da un altro thread e la accept()chiamata genererà a SocketException.


2
Grazie, così ovvio, non mi è nemmeno venuto in mente! Stavo chiamando close () dopo essere uscito dal loop.
lukeo05,

4
Strano che non ci siano queste informazioni nei documenti: download.oracle.com/javase/6/docs/api/java/net/… il metodo non è contrassegnato come il lancio di SocketException. È menzionato solo qui download.oracle.com/javase/1.4.2/docs/api/java/net/…
Vladislav Rastrusny

1
Va bene chiamare close(), voglio dire che genererà l'eccezione nel farlo, quindi esiste un altro modo (che non genera un'eccezione, anche non basato sul timeout) per interrompere l'ascolto delle richieste?
Kushal,

3
E cosa fare se la connessione era già stata accettata e il thread è in attesa di alcuni dati: while ((s = in.readLine ())! = Null)?
Alex Fedulov,

1
@AlexFedulov Spegni quel socket per l'input. readLine()restituirà quindi null e si verificheranno le normali operazioni di chiusura che il thread dovrebbe già avere in atto.
Marchese di Lorne,

31

Attiva il timeout accept(), quindi la chiamata eseguirà il timeout del blocco dopo il tempo specificato:

http://docs.oracle.com/javase/7/docs/api/java/net/SocketOptions.html#SO_TIMEOUT

Imposta un timeout per le Socketoperazioni di blocco :

ServerSocket.accept();
SocketInputStream.read();
DatagramSocket.receive();

L'opzione deve essere impostata prima di entrare in un'operazione di blocco per avere effetto. Se il timeout scade e l'operazione continua a bloccarsi, java.io.InterruptedIOExceptionviene sollevata. In Socketquesto caso non è chiuso.



4

Puoi semplicemente creare un socket "void" per break serverocket.accept ()

Lato server

private static final byte END_WAITING = 66;
private static final byte CONNECT_REQUEST = 1;

while (true) {
      Socket clientSock = serverSocket.accept();
      int code = clientSock.getInputStream().read();
      if (code == END_WAITING
           /*&& clientSock.getInetAddress().getHostAddress().equals(myIp)*/) {
             // End waiting clients code detected
             break;
       } else if (code == CONNECT_REQUEST) { // other action
           // ...
       }
  }

Metodo per interrompere il ciclo del server

void acceptClients() {
     try {
          Socket s = new Socket(myIp, PORT);
          s.getOutputStream().write(END_WAITING);
          s.getOutputStream().flush();
          s.close();
     } catch (IOException e) {
     }
}

4

Il motivo ServerSocket.close()genera un'eccezione è perché hai un outputstreamo un inputstream allegato a quel socket. È possibile evitare questa eccezione in modo sicuro chiudendo prima i flussi di input e output. Quindi prova a chiudere il ServerSocket. Ecco un esempio:

void closeServer() throws IOException {
  try {
    if (outputstream != null)
      outputstream.close();
    if (inputstream != null)
      inputstream.close();
  } catch (IOException e1) {
    e1.printStackTrace();
  }
  if (!serversock.isClosed())
    serversock.close();
  }
}

È possibile chiamare questo metodo per chiudere qualsiasi socket da qualsiasi luogo senza ottenere un'eccezione.


7
I flussi di input e output non sono collegati al ServerSocketma a Sockete stiamo parlando di chiudere il ServerSocketnon il Socket, quindi il ServerSocketpuò essere chiuso senza chiudere Socketi flussi di a.
Icza,


1

OK, l'ho fatto funzionare in un modo che affronta la domanda dell'OP in modo più diretto.

Continua a leggere oltre la breve risposta per un esempio di discussione su come lo uso.

Risposta breve:

ServerSocket myServer;
Socket clientSocket;

  try {    
      myServer = new ServerSocket(port)
      myServer.setSoTimeout(2000); 
      //YOU MUST DO THIS ANYTIME TO ASSIGN new ServerSocket() to myServer‼!
      clientSocket = myServer.accept();
      //In this case, after 2 seconds the below interruption will be thrown
  }

  catch (java.io.InterruptedIOException e) {
      /*  This is where you handle the timeout. THIS WILL NOT stop
      the running of your code unless you issue a break; so you
      can do whatever you need to do here to handle whatever you
      want to happen when the timeout occurs.
      */
}

Esempio nel mondo reale:

In questo esempio, ho un ServerSocket in attesa di una connessione all'interno di un thread. Quando chiudo l'app, voglio chiudere il thread (più specificamente, il socket) in modo pulito prima di lasciare l'app, quindi uso .setSoTimeout () su ServerSocket quindi utilizzo l'interrupt che viene generato dopo il timeout per verificare e vedere se il genitore sta tentando di chiudere il thread. In tal caso, quindi ho chiuso il socket, quindi ho impostato un flag che indica che il thread è terminato, quindi esco dal loop dei thread che restituisce un valore null.

package MyServer;

import javafx.concurrent.Task;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

import javafx.concurrent.Task;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

public class Server {

public Server (int port) {this.port = port;}

private boolean      threadDone        = false;
private boolean      threadInterrupted = false;
private boolean      threadRunning     = false;
private ServerSocket myServer          = null;
private Socket       clientSocket      = null;
private Thread       serverThread      = null;;
private int          port;
private static final int SO_TIMEOUT    = 5000; //5 seconds

public void startServer() {
    if (!threadRunning) {
        serverThread = new Thread(thisServerTask);
        serverThread.setDaemon(true);
        serverThread.start();
    }
}

public void stopServer() {
    if (threadRunning) {
        threadInterrupted = true;
        while (!threadDone) {
            //We are just waiting for the timeout to exception happen
        }
        if (threadDone) {threadRunning = false;}
    }
}

public boolean isRunning() {return threadRunning;}


private Task<Void> thisServerTask = new Task <Void>() {
    @Override public Void call() throws InterruptedException {

        threadRunning = true;
        try {
            myServer = new ServerSocket(port);
            myServer.setSoTimeout(SO_TIMEOUT);
            clientSocket = new Socket();
        } catch (IOException e) {
            e.printStackTrace();
        }
        while(true) {
            try {
                clientSocket = myServer.accept();
            }
            catch (java.io.InterruptedIOException e) {
                if (threadInterrupted) {
                    try { clientSocket.close(); } //This is the clean exit I'm after.
                    catch (IOException e1) { e1.printStackTrace(); }
                    threadDone = true;
                    break;
                }
            } catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
};

}

Quindi, nella mia classe Controller ... (mostrerò solo il codice pertinente, massaggiarlo nel tuo codice secondo necessità)

public class Controller {

    Server server = null;
    private static final int port = 10000;

    private void stopTheServer() {
        server.stopServer();
        while (server.isRunning() {
        //We just wait for the server service to stop.
        }
    }

    @FXML private void initialize() {
        Platform.runLater(()-> {
            server = new Server(port);
            server.startServer();
            Stage stage = (Stage) serverStatusLabel.getScene().getWindow();
            stage.setOnCloseRequest(event->stopTheServer());
        });
    }

}

Spero che questo aiuti qualcuno lungo la strada.


0

Un'altra cosa che puoi provare, che è più pulita, è controllare un flag nel ciclo di accettazione, e quindi quando il tuo thread di amministrazione vuole eliminare il blocco del thread su Accept, imposta il flag (rendilo sicuro) e quindi crea un socket client collegamento alla presa di ascolto. L'accettazione interromperà il blocco e restituirà il nuovo socket. Puoi elaborare alcune semplici cose del protocollo che dicono al thread di ascolto di uscire dal thread in modo pulito. E quindi chiudere il socket sul lato client. Nessuna eccezione, molto più pulito.


questo non ha senso ... quando hai un codice come questo socket = serverSocket.accept (); in quel momento, il blocco inizia e il ciclo non fa parte del nostro codice, quindi c'è modo di far sì che quel codice di blocco cerchi un flag che ho impostato ... almeno non sono stato in grado di trovare un modo per farlo. .. se hai un codice funzionante, per favore condividi?
Michael Sims,

Sì, sembrerebbe che ho lasciato fuori un pezzo cruciale di informazioni che non avrebbe molto senso. Dovresti rendere il socket di accettazione non bloccante e bloccarlo solo per un certo periodo di tempo prima che il loop si ripeta, in quale area verifichi quindi il flag di uscita.
stu

In che modo, esattamente, rendi la presa di accettazione non bloccante?
Michael Sims,

lungo su = 1L; if (ioctl (socket, (int) FIONBIO, (char *) & on))
stu

@stu Questa domanda è per i socket di Java.
Kröw,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.