it-swarm.com.de

Wie kann ich eine ServerSocket-Methode Accept () unterbrechen?

In meinem Haupt-Thread habe ich eine while(listening)-Schleife, die accept() für mein ServerSocket-Objekt aufruft, dann einen neuen Client-Thread startet und einer Collection hinzufügt, wenn ein neuer Client akzeptiert wird.

Ich habe auch einen Admin-Thread, den ich zum Ausgeben von Befehlen verwenden möchte, wie 'exit', der dazu führt, dass alle Client-Threads heruntergefahren werden, sich selbst herunterfahren und der Haupt-Thread herunterfährt.

Der Aufruf accept() in der Schleife while(listening) blockiert jedoch, und es scheint keine Möglichkeit zu geben, ihn zu unterbrechen. Daher kann die while-Bedingung nicht erneut geprüft werden und das Programm kann nicht beendet werden!

Gibt es einen besseren Weg, dies zu tun? Oder eine Möglichkeit, die Sperrmethode zu unterbrechen?

131
lukeo05

Sie können close() von einem anderen Thread aus aufrufen, und der accept() - Aufruf wirft eine SocketException .

139

Setze Timeout auf accept(), dann wird die Blockierung nach einer bestimmten Zeit beendet:

http://docs.Oracle.com/javase/7/docs/api/Java/net/SocketOptions.html#SO_TIMEOUT

Legen Sie ein Zeitlimit für das Blockieren von Socket-Vorgängen fest:

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

Die Option muss vor dem Eintritt in einen Sperrvorgang gesetzt werden, um wirksam zu werden. Wenn das Zeitlimit abläuft und der Vorgang weiterhin blockiert, wird Java.io.InterruptedIOException ausgelöst. Die Socket ist in diesem Fall nicht geschlossen.

27
Juha Syrjälä

Ist der Aufruf von close() auf der ServerSocket eine Option?

http://Java.Sun.com/j2se/6/docs/api/Java/net/ServerSocket.html#close%28%29

Schließt diese Steckdose. Jeder Thread, der derzeit in accept () blockiert ist, löst eine SocketException aus. 

9
Lauri Lehtinen

ServerSocket.close() löst eine ausnahme .__ aus. Der Grund dafür ist, dass Sie einen outputstream oder einen inputstream An diesen Socket angeschlossen haben . Sie können diese Ausnahme sicher vermeiden, indem Sie zuerst die Eingabe- und Ausgabeströme schließen .__ Schließen Sie dann ServerSocket. Hier ein Beispiel:

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();
  }
}

Sie können diese Methode aufrufen, um jeden Socket von überall aus zu schließen, ohne eine Ausnahme zu erhalten.

3
Soham Malakar

Sie können einfach "void" Socket für break serversocket.accept () erstellen.

Serverseite

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
           // ...
       }
  }

Methode für einen Unterbrechungsserverzyklus

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

OK, ich habe das so bekommen, dass die Frage des OP direkter angegangen wird.

Lies weiter über die kurze Antwort auf ein Thread-Beispiel, wie ich das benutze.

Kurze Antwort:

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.
      */
}

Beispiel aus der realen Welt:

In diesem Beispiel wartet ein ServerSocket auf eine Verbindung in einem Thread. Wenn ich die App schließe, möchte ich den Thread (genauer gesagt den Socket) sauber schließen, bevor ich die App schließen lasse. Daher verwende ich das .setSoTimeout () auf dem ServerSocket und dann den Interrupt, der ausgelöst wird Überprüfen Sie nach dem Timeout, ob das übergeordnete Element versucht, den Thread herunterzufahren. Wenn ja, dann setze ich den close-Socket und dann ein Flag, das anzeigt, dass der Thread fertig ist. Dann breche ich aus der Threads-Schleife aus, die eine Null zurückgibt.

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;
    }
};

}

Dann in meiner Controller-Klasse ... (Ich zeige nur relevanten Code an und massiere ihn bei Bedarf in Ihren eigenen Code.)

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());
        });
    }

}

Ich hoffe, das hilft jemandem die Straße runter.

0
Michael Sims

Eine andere Sache, die Sie versuchen können, die sauberer ist, besteht darin, ein Flag in der Annahmeschleife zu überprüfen. Wenn Ihr Admin-Thread den Thread blockieren möchte, der das Accept blockiert, setzen Sie das Flag (machen es Thread sicher) und erstellen dann einen Client-Socket Verbindung zum Abhörsockel . Das Accept stoppt das Blockieren und gibt den neuen Socket zurück Sie können ein paar einfache Protokolle herausfinden, die den Abhör-Thread dazu auffordern, den Thread sauber zu beenden. Dann schließen Sie den Socket an die Client-Seite ... Keine Ausnahmen, viel sauberer.

0
stu