Як я можу перервати метод прийняття () ServerSocket?


143

У головному потоці у мене є while(listening)цикл, який викликає accept()мій об’єкт ServerSocket, потім запускає новий клієнтський потік і додає його до колекції, коли новий клієнт приймається.

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

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

Чи є кращий спосіб зробити це? Або якимось чином перервати метод блокування?



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

Відповіді:


151

Ви можете зателефонувати close()з іншої нитки, і accept()виклик кине a SocketException.


2
Дякую, так очевидно, мені навіть не прийшло в голову! Я дзвонив close () після виходу з циклу.
lukeo05

4
Дивно, що в документах немає цієї інформації: метод download.oracle.com/javase/6/docs/api/java/net/… не позначається як передача SocketException. Тут згадується лише download.oracle.com/javase/1.4.2/docs/api/java/net/…
Владислав Раструсний

1
Чи добре це зателефонувати close(), я маю на увазі, що він викине виключення, коли це робити, тож чи є інший спосіб (який не кидає виняток, також не заснований на тайм-ауті), щоб перестати слухати запити?
Кушаль

3
І що робити, якщо з'єднання було прийнято і потік чекає деяких даних: while ((s = in.readLine ())! = Null)?
Олексій Федулов

1
@AlexFedulov Вимкнення цієї розетки для введення. readLine()тоді повернеться null, і відбудуться звичайні операції закриття, які нитка вже повинна мати місце.
Маркіз Лорн

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" для розбиття 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
Вхідні та вихідні потоки не приєднані до, ServerSocketа до а, Socketі ми говоримо про закриття ServerSocketне Socket, тому ServerSocketможна закрити, не закриваючи Socketпотоки 's.
icza


1

Гаразд, я працював таким чином, щоб більш безпосередньо вирішувати питання ОП.

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

Коротка відповідь:

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

Приклад реального світу:

У цьому прикладі у мене є серверний сервер, який чекає з'єднання всередині теми. Коли я закриваю додаток, я хочу відключити потік (точніше, сокет) чистим способом, перш ніж я дозволяю програмі закриватися, тому я використовую .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;
    }
};

}

Потім у моєму класі 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

Інша річ, яку ви можете спробувати чистіше, - це перевірити прапор у циклі прийняття, а потім, коли ваш адміністративний потік захоче вбити нитку, блокуючу в акцепті, встановіть прапор (зробіть його потоком безпечним), а потім зробіть клієнтський сокет підключення до розетки прослуховування. Приймання припинить блокування та поверне нову розетку. Ви можете розробити якусь просту річ з протоколом, яка розповість потоці прослуховування вийти з потоку чисто. А потім закрийте розетку на стороні клієнта. Немає винятків, набагато чистіше.


це не має сенсу ... коли у вас є такий код, як цей socket = serverSocket.accept (); в цей момент блокування починається, і цикл не є частиною нашого коду, тому є спосіб, щоб цей блокувальний код шукав прапор, який я встановив ... принаймні я не зміг знайти спосіб це зробити. .. якщо у вас є робочий код, будь ласка, поділіться?
Майкл Сімс

Так, здавалося б, я залишив один важливий фрагмент інформації, який би не мав особливого сенсу. Вам потрібно буде зробити сокет прийняття не блокуючим і блокувати лише протягом певного періоду часу, перш ніж цикл повториться, в якій області ви потім перевірите наявність прапора виходу.
Ст

Як саме ви робите приймальний сокет, не блокуючи?
Майкл Сімс

довгий = 1L; if (ioctl (socket, (int) FIONBIO, (char *) & on))
stu

@stu Це питання стосується розеток Java.
Kröw
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.