web-dev-qa-db-fra.com

Comment puis-je interrompre une méthode ServerSocket accept ()?

Dans mon thread principal, j'ai une boucle while(listening) qui appelle accept() sur mon objet ServerSocket, puis démarre un nouveau thread client et l'ajoute à une collection lorsqu'un nouveau client est accepté.

J'ai également un thread administrateur que je souhaite utiliser pour émettre des commandes, telles que 'exit', qui entraînera l'arrêt de tous les threads clients, de s'éteindre et d'éteindre le thread principal en activant false pour écouter.

Cependant, l'appel accept() dans les boucles de la boucle while(listening) et il ne semble y avoir aucun moyen de l'interrompre. La condition while ne peut plus être vérifiée et le programme ne peut pas quitter!

Y a-t-il une meilleure manière de faire cela? Ou un moyen d'interrompre la méthode de blocage?

131
lukeo05

Vous pouvez appeler close() depuis un autre thread, et l'appel accept() lancera un SocketException .

139
Simon Groenewolt

Définissez le délai d’exécution sur accept(), l’appel expirera le blocage après le délai spécifié:

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

Définissez un délai d'expiration pour le blocage des opérations Socket:

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

L'option doit être définie avant d'entrer dans une opération de blocage pour prendre effet. Si le délai expire et que l'opération continue à bloquer, Java.io.InterruptedIOException est déclenché. La Socket n'est pas fermée dans ce cas.

27
Juha Syrjälä

Est-ce qu'appeler close() sur la ServerSocket est une option?

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

Ferme cette prise. Tout thread actuellement bloqué dans accept () lève une exception SocketException. 

9
Lauri Lehtinen

La raison pour laquelle ServerSocket.close() lève exception Est parce que vous avez une outputstream ou une inputstream Attachée à ce socket . Vous pouvez éviter cette exception en toute sécurité en fermant d'abord les flux d'entrée et de sortie ..__ Essayez ensuite de fermer la ServerSocket. Voici un exemple:

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

Vous pouvez appeler cette méthode pour fermer n'importe quel socket de n'importe où sans obtenir d'exception.

3
Soham Malakar

Vous pouvez simplement créer un socket "void" pour break ServerSocket.accept ()

Du côté serveur

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

Méthode pour interrompre le cycle du serveur

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

OK, je travaille de manière à répondre plus directement à la question du PO.

Continuez à lire au-delà de la réponse courte pour un exemple de fil de la façon dont je l'utilise.

Réponse courte:

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

Exemple du monde réel:

Dans cet exemple, un ServerSocket attend une connexion dans un thread. Lorsque je ferme l'application, je veux fermer le thread (plus précisément, le socket) de manière propre avant de laisser l'application se fermer. J'utilise donc .setSoTimeout () sur ServerSocket, puis j'utilise l'interruption émise. après le délai d'attente pour vérifier et voir si le parent essaie de fermer le thread. Si c'est le cas, je ferme alors le socket, puis un drapeau indiquant que le thread est terminé, puis je quitte la boucle Threads qui renvoie 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;
    }
};

}

Ensuite, dans ma classe de contrôleur ... (je ne montrerai que le code pertinent, le transformera en votre propre code si nécessaire)

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

}

J'espère que cela aide quelqu'un sur la route.

0
Michael Sims

Une autre chose que vous pouvez essayer, qui est plus propre, consiste à vérifier un indicateur dans la boucle d'acceptation, puis, lorsque votre thread d'administration souhaite supprimer le blocage du thread sur l'acceptation, définissez l'indicateur (assurez-vous que le thread est sécurisé), puis créez un socket client. connexion au socket en écoute . L'accept va cesser de bloquer et renvoyer le nouveau socket . Vous pouvez résoudre un simple problème de protocole en demandant au thread d'écoute de quitter le thread proprement . Ensuite, fermez le socket le côté client . Aucune exception, beaucoup plus propre.

0
stu