ServerSocket - 是否真的有必要关闭()它?

时间:2013-06-29 22:56:19

标签: java serversocket

我有这个可恶的结构:

public void run() {

        try {
            if (!portField.getText().equals("")) {              
                String p = portField.getText();
                CharSequence numbers = "0123456789";

            btnRun.setEnabled(false);

            if (p.contains(numbers)) {
                ServerSocket listener = new ServerSocket(Integer.parseInt(p));

                while (true) {
                    Socket socket = listener.accept();
                    try {
                        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
                        out.println("Hi there, human.");    
                    } finally {
                        socket.close(); 
                    }
                }} else {
                    JOptionPane.showMessageDialog(null, "Only numbers are allowed.");
                }
            }

        } catch (NumberFormatException | HeadlessException | IOException e) {
            e.printStackTrace();
        } 
    }

正如您所看到的,我需要完全像使用套接字一样关闭侦听器。问题是,如果我在循环后尝试这样做,代码将“无法访问”,如果我尝试在ServerSocket的任何地方声明一个字段,我会得到一个NullPointerException。我不想与socket / client一起关闭ServerSocket,因为我想建立新的连接。

这就是我的问题:

在这种情况下关闭ServerSocket真的很必要吗?当软件关闭时,ServerSocket会自行关闭? (System.exit(0))。如果因为我没有关闭它而关闭软件时ServerSocket继续运行,那么我就有问题了,因为我无法通过那段血腥的代码来关闭它:)。

3 个答案:

答案 0 :(得分:11)

是。虽然销毁对套接字的引用可能导致垃圾收集器完成它,但并未指定它将被关闭。这在许多情况下都是特定于实现的,并且由于性能甚至是难以跟踪的微小错误,有时会相对于设计脱轨。

保持任何地方的参考是一个肯定的投注,即使是WeakReferences也不会。

现在,操作系统可以提供有限的(由于它的设计)插座数量。当您打开越来越多的套接字时,操作系统会受到压力并最终耗尽套接字,导致Java或其他程序失败。另外,根据您或默认设置可能设置的套接字选项,此套接字可能会发送keepalive,也会耗尽其他端点上的资源。

退出时,套接字会在其构造函数中注册关闭操作以关闭它,和/或操作系统的自动清理来关闭它。

你应该从不依赖OS / JVM行为或敲定来关闭你的套接字,特别是如果你有多个套接字,即使你不打算全部使用他们在同一时间。

答案 1 :(得分:7)

是的,有必要发布任何有限的资源,否则你的应用程序会让主机上的其他进程匮乏资源,并且无法长期维持

以下是我执行此类任务的方式:

public void run() {
    ServerSocket serverSocket = null;
    try {
        serverSocket = ... // init here
    } catch (...) {

    } finally {
        if (serverSocket != null) {
            try {
                serverSocket.close();
            } catch (IOException e) {
                // log error just in case
            }
        }
    }
}

此外,将GUI代码移动到其他类是一个不错的主意,以保持清洁。

答案 2 :(得分:3)

一个答案是你真的应该close()资源......即使它不是绝对必要的。

为什么?

因为(假设)意味着并非严格必要的情况可能会发生变化!

并且......让我们面对现实......编写代码以自动关闭服务器套接字 easy

但请继续阅读...


  

在这种情况下关闭ServerSocket真的很必要吗?

取决于。

  • 如果您知道某些 run()只会运行一次然后应用程序总是会完全关闭(下面的模数警告),那么关闭ServerSocket不会造成任何实际伤害。

  • 否则,无法关闭()可能造成伤害。

“伤害”可能有两种形式:

  • 服务器套接字可以在端口上保持“锁定”,防止应用程序的另一个实例侦听该端口(取决于操作系统)。

  • 服务器套接字可能是有限的资源,如果多次调用run()方法(没有close()),应用程序可以“抓取”该资源的所有可用实例,阻止其他应用程序获取实例。

如果多次调用run(),也可能会对应用程序实例本身造成伤害。


  

当软件关闭时,ServerSocket会自行关闭吗?

ServerSocket不会“自我关闭”,除非它被垃圾收集并最终确定。当JVM退出时,您不能依赖于任何一种情况。但从更广泛的意义上讲,当JVM退出时,底层服务器套接字资源通常自动关闭(释放)。

但完整的答案实际上取决于“软件关闭”的含义,以及平台的性质。例如:

  • 如果关闭意味着JVM完全退出,并且“拥有”底层服务器套接字的相应进程(从OS角度来看)退出,则操作系统将关闭该套接字......至少在现代Unix / Linux / Windows平台上。 (注意Android是引擎盖下的Linux)

  • 如果“软件”类似于在Web容器中运行的webapp,“关闭”可能意味着与JVM退出不同,并且ServerSocket可能在关闭后继续存在(并且不会关闭)

  • 如果JVM嵌入其他东西(例如在大型C / C ++应用程序中)并且其他东西没有退出,则操作系统将不知道是否释放底层服务器套接字。

  • 如果您运行的操作系统不提供与Unix / Linux /(现代)Windows相同级别的进程分离/资源管理,那么底层服务器套接字可能不是在进程退出时由OS关闭。 (此场景可能适用于嵌入式系统上的Java实现...)


无论如何,做正确的事并不难。在Java 7及更高版本中:

public void run() {
    String p = portField.getText().trim();
    if (p.isEmpty()) {
        return;
    }
    btnRun.setEnabled(false);
    try (ServerSocket listener = new ServerSocket(Integer.parseInt(p))) {
        while (true) {
            try (Socket socket = listener.accept();
                 PrintWriter out = new PrintWriter(
                         socket.getOutputStream(), true)) {
                out.println("Hi there, human.");    
            }
        }
    } catch (NumberFormatException e) {
        JOptionPane.showMessageDialog(null, "Only numbers are allowed.");
    } catch (HeadlessException | IOException e) {
        e.printStackTrace();
    } finally {
        btnRun.setEnabled(true); // ... possibly
    }
}

(尽管如此,我没有看到抓住HeadlessException看起来像是从按钮监听器调用的动作。)