【问题标题】:Close on socket's input stream takes too long关闭套接字的输入流需要太长时间
【发布时间】: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();
      }
    }

【问题讨论】:

  • 您能否分享一下您在哪里实例化套接字、在哪里关闭套接字以及在哪里尝试重新启用它的代码?

标签: java sockets tcp


【解决方案1】:

关闭套接字的输入流会关闭套接字和输出流,但不会刷新它。唯一影响套接字的实际操作是关闭它。除非您弄乱了 SO_LINGER 选项,否则关闭套接字是异步的,因此通常不会出现您描述的情况。

结论:你搞砸了 SO_LINGER 选项。

解决办法:不要。

你说服务器在重新连接之前无法检测到客户端问题。

结论:当这些读取超时发生时,您并没有关闭客户端应用程序中的套接字。

解决方法:关闭它。然后服务器将读取 EOS,关闭套接字,然后继续其愉快的方式。

关闭套接字的输入流是不必要的。只需关闭您包裹在套接字输出流周围的最外层输出流/写入器,以确保它被刷新,并且可能在 finally 块中的套接字本身。

其余部分提出的问题多于答案。

  1. 您如何“在 Java 应用程序中完全禁用套接字超时”?
  2. 您说您的服务器正在“清理”,但您还说它“一开始还没有启动关闭”。为什么不呢?

【讨论】:

  • 嗨,EJP。首先感谢您的回答。一开始我可能没有说清楚。请查看我编辑的问题以获取有关您问题的答案。
  • 嗨,EJP。我们的客户端应用程序关闭了套接字,但我们的服务器无论出于何种原因都无法检测到 EOS。这就是我们的假设基于对客户代理的一些奇怪反应的地方。如果代理丢弃了发送给我们的 FIN 或 RST 包,那么我们的服务器就无法检测到这种情况。我也不相信关闭是异步操作。关闭还必须等待来自客户端的一些 ACK。如果这些 ACK 被丢弃,那么关闭需要很长时间。 Java 的源代码在关闭时有 cmets 警告可能的长操作。
  • 您似乎声称 DataInputStream.close() 需要 15 分钟。它没有。那么有什么作用呢?你的问题还不清楚。
  • 由于您似乎完全相信 DataInputStream.close() 应该异步执行这一事实,我将再次尝试更仔细地查看我们的日志。我会告诉你的!!!
  • 我再次查看了我们的服务器日志。在 DataInputStream.close() 之前有一个日志语句,注销意图关闭输入流,然后在 DataOutpuStream.close() 之前有另一个日志,注销意图关闭输入流。 2 个日志语句(都来自同一个线程!!!)之间的差异是 15 分钟。毫无疑问!!!什么可能导致这种延迟?不会是代理或路由器丢了一些消息吗?
猜你喜欢
  • 1970-01-01
  • 2018-12-11
  • 2021-04-08
  • 1970-01-01
  • 2013-12-17
  • 2011-04-26
  • 1970-01-01
  • 2012-03-17
  • 1970-01-01
相关资源
最近更新 更多