我有这个可恶的结构:
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继续运行,那么我就有问题了,因为我无法通过那段血腥的代码来关闭它:)。
答案 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
看起来像是从按钮监听器调用的动作。)