Как я могу прервать метод ServerSocket accept ()?

в моем основном потоке у меня есть while(listening) цикл, который называет accept() на моем объекте ServerSocket затем запускает новый поток клиента и добавляет его в коллекцию, когда принимается новый клиент.

у меня также есть поток администратора, который я хочу использовать для выдачи команд, таких как "exit", который заставит все клиентские потоки быть закрытыми, закрыть себя и закрыть основной поток, повернув прослушивание false.

на accept() вызов в while(listening) петли блоков, и, похоже, нет никакого способа прервать его, поэтому условие while не может быть проверено снова, и программа не может выйти!

есть ли лучший способ сделать это? Или каким-то образом прервать блокировку?

8 ответов


можно назвать close() из другого потока, и accept() вызов бросит SocketException.


установить тайм-аут на accept(), то вызов будет тайм-аут блокировки после заданного времени:

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

установить тайм-аут на блокировку Socket операции:

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

параметр должен быть установлен до ввода операции блокировки, чтобы вступить в силу. Если тайм-аут истекает и операция будет продолжать блокировать,java.io.InterruptedIOException поднимается. The Socket is в данном случае не закрыто.


называет close() на ServerSocket вариант?

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

закрывает этот разъем. Любой поток, заблокированный в accept (), вызовет исключение SocketException.


вы можете просто создать сокет "void" для break serversocket.accept ()

сервер

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

метод прерывания цикла сервера

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

причина ServerSocket.close() выдает исключение это потому что у вас есть outputstream или inputstream прикреплен к этой розетке. Этого исключения можно избежать, предварительно закрыв входные и выходные потоки. Затем попробуйте закрыть ServerSocket. Вот пример:

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

вы можете вызвать этот метод, чтобы закрыть любой розетки в любом месте без исключения.


еще одна вещь, которую вы можете попробовать, которая чище, - это проверить флаг в цикле accept, а затем, когда ваш поток администратора хочет убить блокировку потока на accept, установите флаг (сделайте его потокобезопасным), а затем сделайте подключение клиентского сокета к прослушивающему сокету. Accept прекратит блокировку и вернет новый сокет. Вы можете разработать какой-то простой протокол, сообщающий слушающему потоку, чтобы выйти из потока чисто. А затем закройте сокет на стороне клиента. Никаких исключений, много уборщик.



хорошо, я получил это работает таким образом, что решает вопрос OP более непосредственно.

продолжайте читать мимо короткого ответа для примера потока, как я использую это.

короткий ответ:

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

реальный пример:

в этом примере у меня есть ServerSocket, ожидающий соединения внутри потока. Когда я закрываю приложение, я хочу закрыть поток (более конкретно, сокет) чистым способом, прежде чем я позволю приложению близко, поэтому я использую .setSoTimeout () на ServerSocket затем я использую прерывание, которое бросается после тайм-аута, чтобы проверить и посмотреть, пытается ли родитель закрыть поток. Если это так, то я устанавливаю закрыть сокет, затем установите флаг, указывающий, что поток сделан, затем я вырываюсь из цикла потоков, который возвращает 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;
    }
};

}

тогда, в моем классе контроллера ... (Я только покажу соответствующий код, массируйте его в свой собственный код по мере необходимости)

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

}

Я надеюсь это помогает кому-то в будущем.