Как закрыть розетку надлежащим образом?

Это простой TCP-сервер. Как я могу закрыть сокет, когда программа завершена? Я использую try/finally и пытаюсь закрыть сокет. Но при выходе из программы он не запускает блок finally.

Кто-нибудь может подумать о том, как правильно закрыть розетку?

try {
        socket = new ServerSocket(port);
        System.out.println("Server is starting on port " + port + " ...");
    }catch (IOException e){
        System.out.println("Error on socket creation!");
    }

    Socket connectionSocket = null;
    try{
        while(true){            
            try{
                connectionSocket = socket.accept();
                Thread t =  new Thread(new ClientConnection(connectionSocket));
                t.start();
            }catch (IOException e) {
                System.out.println("Error on accept socket!");
            }
        }
    }finally{
        this.socket.close();
        System.out.println("The server is shut down!");
    }

Ответы

Ответ 1

После создания ServerSocket вы можете добавить ShutdownHook, чтобы закрыть его при завершении JVM, примерно так:

Runtime.getRuntime().addShutdownHook(new Thread(){public void run(){
    try {
        socket.close();
        System.out.println("The server is shut down!");
    } catch (IOException e) { /* failed */ }
}});

Вызов ServerSocket # закрыть завершает блокировку ServerSocket.accept, вызывая его исключение SocketException. Однако обратите внимание, что ваша текущая обработка IOException в цикле while означает, что вы снова войдете в цикл while, чтобы попытаться принять на закрытом сокете. JVM все равно будет завершен, но он немного неопрятен.

Крюки выключения не запускаются, если вы завершаете консольное приложение в Eclipse (по крайней мере на Windows). Но они запускаются, если вы CTRL-C Java в обычной консоли. Для их запуска вам необходимо, чтобы JVM прерывался нормально, например. SIGINT или SIGTERM, а не SIGKILL (kill -9).

Простая программа, которую вы можете выполнить в Eclipse или консоли, продемонстрирует это.

public class Test implements Runnable {

  public static void main(String[] args) throws InterruptedException {
    final Test test = new Test();
    Runtime.getRuntime().addShutdownHook(new Thread(){public void run(){
      test.shutdown();
    }});
    Thread t = new Thread(test);
    t.start();
  }

  public void run() {
    synchronized(this) {
      try {
        System.err.println("running");
        wait();
      } catch (InterruptedException e) {}
    }
  }

  public void shutdown() {
    System.err.println("shutdown");
  }
}

Ответ 2

Нет необходимости в вашем конкретном случае, операционная система закроет все сокеты TCP для вас, когда программа выйдет.

Ответ 3

От javadoc:

Время выполнения Java автоматически закрывает входные и выходные потоки, клиентский сокет и серверный сокет, поскольку они были созданный в инструкции try-with-resources.

Кроме

Метод finalize() вызывается виртуальной машиной Java (JVM) перед выходом программы, чтобы дать программе возможность очистить и релиз ресурсов. Многопоточные программы должны закрывать все файлы и Сокеты, которые они используют перед выходом, так что они не сталкиваются с ресурсом голодание. Вызов метода server.close() в методе finalize() закрывается соединение Socket, используемое каждым потоком в этой программе.

protected void finalize(){
//Objects created in run method are finalized when
//program terminates and thread exits
     try{
        server.close();
    } catch (IOException e) {
        System.out.println("Could not close socket");
        System.exit(-1);
    }
  }

Ответ 4

Howcome, наконец, не запущен? Вероятно, while (true) следует заменить чем-то вроде

while (!shutdownRequested)

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

Ответ 5

Ну, как вы "выходите" из программы? finally будет выполняться, если будет выбрано исключение или если блок try завершит свое выполнение "обычным" способом, но я думаю, что это может быть "сложно" из-за вашего while(true).

Чтобы закрыть сокет, вы должны использовать socket.close(), и я бы рекомендовал вам не полагаться на функцию destroy.

Ответ 6

Класс ServerSocker является AutoCloseable. Поэтому правильный способ использовать ServerSocket экземпляра находится с try -са-ресурсы блоком, так что гнездо надежно закрыто, если исключение. Не используйте блок try... finally: он будет работать неправильно для некоторых крайних случаев (случаев, которые требуют исключенного исключения). Не используйте shutdown-hook: сокет не должен быть глобальным ресурсом, и такой подход будет подвержен гоночным опасностям. try -with-resources является рекомендуемым способом правильного закрытия всех ресурсов ввода-вывода, таких как сокет.

    try (ServerSocket socket = new ServerSocket(...)) {
       ... // use the socket
    } catch (IOException e) {
        ... // exception handling code
    }