Как я могу прервать метод ServerSocket accept ()? - PullRequest
132 голосов
/ 06 июня 2010

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

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

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

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

Ответы [ 8 ]

142 голосов
/ 06 июня 2010

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

27 голосов
/ 06 июня 2010

Установите тайм-аут на 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 повышается. Socket в этом случае не закрывается.

9 голосов
/ 06 июня 2010

Может ли close() вызывать ServerSocket вариант?

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

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

3 голосов
/ 06 июля 2015

Вы можете просто создать «пустой» сокет для перерыва 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) {
     }
}
3 голосов
/ 04 февраля 2014

Причина 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();
  }
}

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

1 голос
/ 20 февраля 2018
0 голосов
/ 19 марта 2018

Ладно, я понял, что это работает таким образом, что более прямо отвечает на вопрос ОП.

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

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

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

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

}

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

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

}

Надеюсь, это поможет кому-то в будущем.

0 голосов
/ 02 декабря 2013

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

...