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只是因为我没有关闭它,那么我有一个问题,因为我无法达到那个血腥的代码来关闭它:)。

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

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

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

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

永远不应该依赖OS / JVM行为或完成来关闭套接字,尤其是如果您有多个套接字,即使您不打算同时使用所有套接字也是如此。

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

这是我如何做这样的任务:

 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代码移动到其他类是一个不错的主意,以保持清洁。

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

为什么?

因为,(假设)意味着它不是绝对必要的情况可能会改变!

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

但请继续阅读……


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

这取决于。

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

  • 否则,关闭()失败可能会造成伤害。

“伤害”可能有两种forms:

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

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

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


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

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

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

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

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

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

  • 如果您运行的操作系统不提供与Unix / Linux /(现代)Windows相同级别的进程分离/资源管理,那么操作系统在进程退出时可能无法关闭底层服务器套接字。 (此方案可能适用于嵌入式系统上的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从按钮监听器调用的动作中捕获HeadlessException的意义。)