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

148

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

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

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

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

lukeo05
источник
Для человека, который хочет подождать x раз, а затем отреагировать, используйте setSoTimeout ().
Абдулла Ораби,

Ответы:

157

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

Саймон Гроенвольт
источник
2
Спасибо, настолько очевидное, что мне даже в голову не пришло! Я звонил close () после выхода из цикла.
lukeo05 06
4
Странно, что в документах нет этой информации: download.oracle.com/javase/6/docs/api/java/net/… метод не помечен как бросающий SocketException. Здесь только упоминается download.oracle.com/javase/1.4.2/docs/api/java/net/…
Владислав Раструсный 07
1
Можно ли позвонить close(), я имею в виду, что при этом будет сгенерировано исключение, так что есть ли другой способ (который не генерирует исключение, также не основанный на тайм-ауте), чтобы прекратить прослушивание запросов?
Кушал
3
А что делать, если соединение уже принято и поток ожидает каких-то данных: while ((s = in.readLine ())! = Null)?
Alex Fedulov
1
@AlexFedulov Выключите этот сокет для ввода. readLine()затем вернет null, и будут выполнены обычные операции закрытия, которые поток уже должен иметь на месте.
Marquis of Lorne
31

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

Юха Сюрьяля
источник
4

Вы можете просто создать сокет "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) {
     }
}
Николай Савченко
источник
4

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

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

Сохам Малакар
источник
7
Потоки ввода и вывода не прикреплены к ServerSocketa, Socketи мы говорим о закрытии ServerSocketnot the Socket, поэтому его ServerSocketможно закрыть, не закрывая Socketпотоки a .
icza 03
1

Хорошо, у меня это работает таким образом, чтобы более прямо решать вопрос 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());
        });
    }

}

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

Майкл Симс
источник
0

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

Стю
источник
это не имеет смысла ... когда у вас есть такой код socket = serverSocket.accept (); в этот момент начинается блокировка, и цикл не является частью нашего кода, поэтому есть способ заставить этот блокирующий код искать установленный мной флаг ... по крайней мере, я не смог найти способ сделать это. .. если у вас есть рабочий код, поделитесь пожалуйста?
Майкл Симс
Да, похоже, я упустил одну важную часть информации, которая делает ее бессмысленной. Вам нужно будет сделать так, чтобы принимающий сокет не блокировался и блокировался только на некоторый период времени, прежде чем цикл повторится, в этой области вы затем должны проверить флаг выхода.
stu
Как именно сделать так, чтобы принимающий сокет был неблокирующим?
Майкл Симс
длинный вкл = 1л; если (ioctl (socket, (int) FIONBIO, (char *) & on))
stu
@stu Этот вопрос касается сокетов Java.
Kröw
-1

Вы можете просто передать предел времени ожидания (миллисекунды) в качестве параметра при вызове функции accept.

например serverSocket.accept (1000); автоматически закрыть запрос через 1 секунду

Дхрув Гоял
источник
accept () не имеет параметров. docs.oracle.com/javase/7/docs/api/java/net/…
cesargastonec,