3

有没有办法获得有关 Java 标准类的异常安全方面的一些详细信息?主要使用 C++ 和 C#,我对 Java 异常规范感到困惑,所以我需要了解处理异常的正确方法。

更具体地说,让我们考虑ServerSocket. 一旦构建了它的对象,它就开始监听传入的连接。然后,您应该使用accept()接受连接(如果有人尝试连接)。

如果您之前使用 配置了服务器套接字,则会setSoTimeout()发生更改,因为在指定的时间段内没有人尝试连接。没关系,服务器套接字仍然可用,所以你只需再次调用。accept()SocketTimeoutExceptionaccept()

SocketTimeoutException并不是唯一accept()可能抛出的东西。所有其他例外是​​什么意思?accept()如果我用 2 个 catch 子句包装调用: for SocketTimeoutExceptionand ,在进入子句后我IOException还能安全地使用相关实例吗?ServerSocketIOException

我非常感谢 Java 范围和特定于 ServerSocket 的答案。

4

6 回答 6

3

重用对象是不安全的。对于这样的问题,我总是会寻找来源,这就是它开放的原因。

因此,如果您查看那个: http: //kickjava.com/src/java/net/ServerSocket.java.htm,您会注意到accept()如果套接字已关闭或不再绑定,则会抛出 SocketException(从 IOException 继承)。这两种状态都表明 ServerSocket 不再有效。

所以对于这个类,一般来说,如果你因异常而失败,总是尝试在 finally 块中优雅地关闭 ServerSocket,然后重新创建它。

此外,在您的 Java 范围内的问题范围内:始终查看源代码并了解接口在做什么。如果是重现行为的任务关键型编写测试(使用如此低级的 api 根本不应该容易)。

最后 - Java 是否一直以这种方式做这些事情?不。有些类是无状态的,有些则不是(如 ServerSocket),有些是线程安全的,有些则不是。而且您需要从文档或(主要)从代码中了解它们构建的状态,以便了解当异常使您脱离主路径时该怎么做。

大多数人诅咒那些检查过的 Java 异常,因为它们中的大多数(与大多数 IOException 一样)并不能真正以有意义的方式恢复。大多数时候,他们争辩说,你无法理解每一个优秀的极端案例。这就是为什么许多复杂的框架可能会重试两次或三次,如果他们认为在这种情况下可能会重试,但最终会向顶层框架层抛出 RuntimeException。他们在那里做了一些有用的事情(提供上下文的有意义的错误)并记录他们拥有的所有细节,这在大多数情况下是一个巨大的堆栈跟踪。许多开发人员担心的大量知识资源。

那么,如果您无法从未经测试的极端情况问题中恢复过来,您该怎么办?抛出(可能带有 RuntimeException 的某个子类)最有意义的堆栈跟踪,并用您拥有的所有上下文进行注释。设置监控。如果您经常遇到问题,请修复它。

于 2011-08-12T23:31:03.740 回答
1

是的。该对象仍然存在 - 可能处于错误状态,在这种情况下,它将引发其他异常,直到纠正为止。

于 2011-08-12T23:25:04.713 回答
1

如果您阅读ServerSocket的规范,您会看到它抛出一个 IOException “如果在等待连接时发生 I/O 错误”。accept()再插上插座安全吗?是的。你会再次抛出相同的异常吗?可能是这样。

于 2011-08-12T23:33:13.073 回答
1

我还没有找到一种简单的方法来查看对象是否仍处于可用状态。每个对象都有自己的规则。

在您使用 ServerSocket 的特定情况下,我会尝试两种不同的方法之一:

  • 运行 netstat 或其他一些实用程序来查看操作系统认为套接字在做什么。如果操作系统认为它没有在听,那么就发生了一些事情。或者

  • 编写一个将抛出异常的测试程序并查看它的作用。我一直这样做(尤其是使用专有软件)。在您选择的 ServerSocket 案例中会更难,因为我能想到的所有场景(例如,正在使用的地址、权限不足等)都不会导致对象仍然有效。

于 2011-08-12T23:39:13.820 回答
1

但是 SocketTimeoutException 并不是 accept() 可能抛出的唯一东西。所有其他例外是​​什么意思?

根据javadoc,声明的异常是IOExceptionSecurityException和。和仅在特定上下文中出现,您不应尝试捕获和处理它们。(它们不是您要尝试从中恢复的问题!)这种情况发生在“一些其他 I/O 错误”发生时。javadoc 没有指定这些 I/O 错误可能是什么,但可能包括以下内容:SocketTimeoutExceptionIllegalBlockingModeExceptionSecurityExceptionIllegalBlockingModeExceptionIOException

  • 您绑定的地址不再有效
  • 发生传输协议错误
  • 操作系统协议栈中发生了一些错误(资源问题、错误...)

(javadoc 没有说明IOException可能抛出哪些子类的事实暗示您不应该尝试做一些聪明的事情来尝试恢复。如果您这样做,您的代码可能依赖于平台。)

如果我用 2 个 catch 子句包装对 accept() 的调用:对于 SocketTimeoutException 和 IOException,在进入 IOException 子句后,我还能安全地使用相关的 ServerSocket 实例吗?

是和不是。从某种意义上说,它是安全的,您不会将应用程序置于比它已经处于的更糟糕的状态。另一方面,不能保证下一次accept调用不会因同样的问题而失败。

如果您的应用程序打算作为无人值守的服务器运行,我认为您别无选择,只能记录并重IOException试......希望问题是暂时的。

于 2011-08-13T00:19:39.780 回答
0

你可以找到你对 javadoc 的答案setsoTimeout,它说:

使用指定的超时启用/禁用 SO_TIMEOUT,以毫秒为单位。将此选项设置为非零超时,对此 ServerSocket 的 accept() 调用将仅阻塞此时间量。如果超时到期,则会引发 java.net.SocketTimeoutException,尽管 ServerSocket 仍然有效。必须在进入阻塞操作之前启用该选项才能生效。超时必须 > 0。超时为零被解释为无限超时。

于 2011-08-12T23:35:58.910 回答