В моем основном потоке у меня есть while(listening)
цикл, который вызывает accept()
мой объект ServerSocket, затем запускает новый клиентский поток и добавляет его в коллекцию, когда новый клиент принят.
У меня также есть поток администратора, который я хочу использовать для выдачи команд, таких как «выход», которые приведут к завершению работы всех клиентских потоков, отключению себя и завершению основного потока, переключив прослушивание на false.
Однако accept()
вызов в while(listening)
цикле блокируется, и, похоже, нет никакого способа его прервать, поэтому условие while не может быть проверено снова, и программа не может выйти!
Есть лучший способ сделать это? Или каким-то способом прервать метод блокировки?
java
networking
blocking
interrupt
lukeo05
источник
источник
Ответы:
Вы можете позвонить
close()
из другого потока, иaccept()
вызов вызовет ошибкуSocketException
.источник
close()
, я имею в виду, что при этом будет сгенерировано исключение, так что есть ли другой способ (который не генерирует исключение, также не основанный на тайм-ауте), чтобы прекратить прослушивание запросов?readLine()
затем вернет null, и будут выполнены обычные операции закрытия, которые поток уже должен иметь на месте.Установите тайм-аут
accept()
, тогда вызов будет отключать блокировку по истечении указанного времени:http://docs.oracle.com/javase/7/docs/api/java/net/SocketOptions.html#SO_TIMEOUT
источник
Позвонить
close()
поServerSocket
опциону?http://java.sun.com/j2se/6/docs/api/java/net/ServerSocket.html#close%28%29
источник
Вы можете просто создать сокет "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(); } }
Вы можете вызвать этот метод, чтобы закрыть любой сокет из любого места без исключения.
источник
ServerSocket
a,Socket
и мы говорим о закрытииServerSocket
not theSocket
, поэтому егоServerSocket
можно закрыть, не закрываяSocket
потоки a .Используйте serverSocket.setSoTimeout (timeoutInMillis) .
источник
Хорошо, у меня это работает таким образом, чтобы более прямо решать вопрос 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, а затем использую генерируемое прерывание по истечении таймаута, чтобы проверить, пытается ли родитель закрыть поток. Если да, то я закрываю сокет, затем устанавливаю флаг, указывающий, что поток завершен, затем я выхожу из цикла потоков, который возвращает ноль.
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()); }); } }
Я надеюсь, что это поможет кому-то в будущем.
источник
Еще одна вещь, которую вы можете попробовать, что чище, - это проверить флаг в цикле принятия, а затем, когда ваш поток администратора хочет убить блокировку потока при принятии, установите флаг (сделайте его потокобезопасным), а затем создайте клиентский сокет подключение к прослушивающему сокету. Принятие прекратит блокировку и вернет новый сокет. Вы можете разработать какую-то простую протокольную вещь, сообщающую слушающему потоку, что он должен полностью выйти из потока. А затем закройте сокет на стороне клиента. Без исключений, намного чище.
источник
Вы можете просто передать предел времени ожидания (миллисекунды) в качестве параметра при вызове функции accept.
например serverSocket.accept (1000); автоматически закрыть запрос через 1 секунду
источник