【发布时间】:2014-02-10 19:17:24
【问题描述】:
我们的一位客户时不时地遇到问题,他们的连接在特定时间后断开。客户端的异常显示“读取超时”消息。由于我们在 Java 应用程序中不使用套接字超时(我们使用默认值 0),因此我们首先想到的是客户端的超时必须来自他们身边的代理或路由器。他们的 IT 部门正在研究这个问题。
但是,当连接中止时,我们的客户端应用程序会关闭套接字,然后与我们的服务器建立新连接。在接收到来自同一客户端的新连接后,服务器将在开始使用新连接之前对旧连接启动清理操作。当服务器检测到 EOS 或任何其他读取异常时,或者当同一个客户端接受新连接并且旧连接由于某种原因未被清理时,就会发生这种清理(这就是由于我们的服务器失败而发生的情况检测 EOS)。该清理操作实际上包括一些日志记录、各种路由数据结构的清理以及输入和输出流的最终关闭。此时我们观察到,旧套接字的输入流的关闭会阻塞 15 分钟,并且时间始终相同。正如您所理解的那样,一切都会出错,因为新连接开始挨饿,并且同样的问题从头开始重复。
现在,我假设我们在服务器端的套接字正遭受不确定的 FIN_WAIT_2 或 TIME_WAIT 状态,其中套接字保持在其中一种状态而没有收到必要的 ACK(可能它们被丢弃),这可能会移动它到关闭状态。虽然我们的服务器不是一开始就断开连接的服务器,但我想是客户端的代理或路由器,它可能已经启动并使它看起来像是我们的服务器关闭了。我已经读过将服务器端的 SO_LINGER 选项设置为 0 在这种情况下可能会有所帮助(尽管通常不建议这样做)。仅供参考:到目前为止,我们还没有弄乱 SO_LINGER 选项,但由于问题,我们正在考虑这样做。
你能解释一下这个理论是否正确吗?此外,为什么使套接字关闭操作需要 15 分钟?这远远超出了正常的 2 * MSL(最大段生命周期)持续时间,这应该是套接字等待关闭的时间。我们是否应该将 SO_LINGER 选项(Java 中的 setSoLinger 方法)的值设置为大于 0 的值?在任何情况下,客户端已经中止了另一端的连接,因此,在关闭套接字之前将 linger 选项设置为 0 不会导致客户端出现任何其他异常或错误状态。此外,您是否有任何工具可以用来模拟我们环境中丢弃的数据包?
进行套接字创建和清理的代码没有什么特别之处。这是一个sn-p:
Socket socket = new Socket(ipAddress, port);
OutputStream dataOutputStream = new BufferedOutputStream(socket.getOutputStream(), 64000);
InputStream dataInputStream = new BufferedInputStream(socket.getInputStream(), 64000);
流关闭代码
try {
if (dataInputStream != null) {
LOGGER.info("Going to close input stream....");
dataInputStream.close();
}
} finally {
if (dataOutputStream != null) {
LOGGER.info(".closeReaderWriter()", "Going to close output stream...");
dataOutputStream.close();
}
}
【问题讨论】:
-
您能否分享一下您在哪里实例化套接字、在哪里关闭套接字以及在哪里尝试重新启用它的代码?