【问题标题】:What could cause socket ConnectException: Connection timed out?什么可能导致套接字 ConnectException:连接超时?
【发布时间】:2011-04-22 02:40:25
【问题描述】:

我们有一个 Webstart 客户端,它通过 HTTPS 使用 java.net.HttpsURLConnection 发送序列化对象来与服务器通信。

在我的本地机器和位于我们办公室的测试服务器上一切正常,但我遇到了一个非常非常奇怪的问题,它只发生在我们的生产和登台服务器上(偶尔会发生)。我所知道的这些服务器和我们办公室的服务器之间的主要区别在于它们位于其他地方并且与它们的客户端-服务器通信要慢得多,但在此之前它在生产中也能正常工作很长时间。

无论如何,这就是正在发生的事情:

  • 客户端在HttpURLConnection上设置读取超时和Content-Type等属性后,在其上调用getOutputStream()以获取要写入的流。
  • 此时,据我所知,客户端挂起一段时间。
  • 然后客户端抛出以下异常:
java.net.ConnectException:连接超时:连接 在 java.net.PlainSocketImpl.socketConnect(本机方法) 在 java.net.PlainSocketImpl.doConnect(未知来源) 在 java.net.PlainSocketImpl.connectToAddress(未知来源) 在 java.net.PlainSocketImpl.connect(未知来源) 在 java.net.SocksSocketImpl.connect(未知来源) 在 java.net.Socket.connect(未知来源) 在 com.sun.net.ssl.internal.ssl.SSLSocketImpl.connect(未知来源) 在 com.sun.net.ssl.internal.ssl.BaseSSLSocketImpl.connect(未知来源) 在 sun.net.NetworkClient.doConnect(未知来源) 在 sun.net.www.http.HttpClient.openServer(未知来源) 在 sun.net.www.http.HttpClient.openServer(未知来源) 在 sun.net.www.protocol.https.HttpsClient.(未知来源) 在 sun.net.www.protocol.https.HttpsClient.New(未知来源) 在 sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(未知来源) 在 sun.net.www.protocol.http.HttpURLConnection.plainConnect(未知来源) 在 sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(未知来源) 在 sun.net.www.protocol.http.HttpURLConnection.getOutputStream(未知来源) 在 sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(未知来源)

请注意,这不是SocketTimeoutExceptionHttpURLConnection 上的connect() 方法表示,如果在建立连接之前超时到期,它会抛出它。此外,当这种情况发生时,我可以拨打 conn.getResponseCode() 并收到 200 的响应代码。

  • 在服务器端,EOFException 被抛出 ObjectInputStream 的构造函数,该构造函数尝试读取序列化标头但失败,因为客户端从未获得要写入的 OutputStream

如果有帮助,以下是在调用 getOutputStream() 之前在 HttpsURLConnection 上进行的调用(经过编辑以仅显示正在进行的调用,而不是执行此操作的代码的整个结构):

HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setUseCaches(false);
conn.setReadTimeout(30000);
conn.setRequestProperty("Cookie", cookie);
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-java-serialized-object");
conn.getOutputStream();

问题是,我不知道这一切是如何发生的,尤其是考虑到它只是偶尔发生(我无法分辨出明确的活动模式),即便如此,也只有当有(相对)客户端和服务器之间的高延迟。

鉴于我目前能够找到的关于 java.net.ConnectException: Connect timed out 的信息,我想知道这是否不是我们的服务器正在运行的网络上的网络或防火墙问题......但这没有多大意义对我来说,该请求显然已通过 servlet。此外,在同一网络上运行的其他应用未报告类似问题。

有谁知道这可能是什么原因,甚至我应该调查什么?

【问题讨论】:

标签: java sockets exception https httpurlconnection


【解决方案1】:

我们遇到了与您类似的情况。通常在高负载下并且不容易在测试中重现。还没有修复它,但这是我们经历的步骤。

如果是防火墙问题,我们会收到 Connection Refused 或 SocketTimeout 异常。

1) 您是否能够在服务器上的访问日志中跟踪这些请求 - 它们是否显示 HTTP 状态 200 或 404 或其他?在我们的例子中,服务器(在这种情况下是 IIS)日志显示客户端关闭了连接,而不是服务器。所以这是一个谜。

更新:如果客户端总是收到 200,那么服务器实际上已经发回了一些响应,但我怀疑响应字节大小(如果这记录在访问日志中)将显示与该请求的正常响应大小不同的值

如果它显示相同大小的响应,那么您有一个(可能不合理)条件,即服务器实际上响应正确,但客户端没有收到响应,因为连接在某处终止介于两者之间。

2) 网络管理员团队查看 TCP/IP 流量以确定哪一端(或中间路由器)正在终止 HTTP/TCP-IP 会话。一旦我们了解终止连接的一端就是看看为什么。有足够知识的人可以运行snoop

3) 服务器上是否配置/限制了最大请求数 - 这是否会限制您的连接?

4) 是否有任何中间负载均衡器可以丢弃请求?

更新:我们还想做但未完成的另一件事是在客户端和服务器之间创建静态路由,以减少中间的跃点数并确保不会出现与网络相关的连接中断。见http://en.wikipedia.org/wiki/Static_routing

5) 另一个建议是设置ConnectTimeout 以查看它们是否具有更高的值。 更新:你可能想试试conn.getErrorStream()

返回错误流,如果 连接失败,但服务器已发送 还是有用的数据。如果 连接未连接,或者如果 服务器没有错误,而 连接或服务器是否有 错误但没有发送错误数据,这 方法将返回 null。

6) 也可以尝试在服务器上每隔 5 秒进行一组线程转储,以查看是否有任何线程在服务器上显示这些传入请求。

更新:截至今天,我们学会了解决这个问题,因为我们每天 400,000 次请求中的失败率为 200-300 次,即 0.00075 %

【讨论】:

  • 感谢您的回答。我不确定服务器访问日志,但我确实编辑了问题以注意客户端在捕获异常后看到响应代码 200。我已经尝试过连接超时值,但据我所知,超过时会抛出SocketTimeoutException(而不是ConnectException)。我不确定其他任何事情,但它们似乎都值得研究。
  • @ColinD:我的更新中的 conn.getErrorStream() 有什么有趣的地方吗?
  • 我还没有机会尝试,但考虑到服务器端发生的情况,它不会写任何东西流回客户端。
  • 我最终不得不设置一个较低的连接超时并调用URLConnection.connect(),这样我可以在超时时重试几次。不理想,但我们无法确定究竟是什么原因造成的。
【解决方案2】:

在我们的服务器上使用它时,我们也会遇到零星的超时。我们可以通过两件事来解决它:

  1. 通过 setFixedLengthStreamingMode 使用特定的 ContentLength(将错误率从 ~150 降低到 10)
  2. 如果发生超时重试(错误率从 10 到 0。最多重试一次后一切正常)

伪代码:

//set timeouts to 6s
try{
 //open connection here and write etc.
 //use a timeout of 6s (since retry is in place)
} 
catch (java.io.InterruptedIOException e) {
 //read- or connection time out try again                 
} 

发生这种情况的另一种理论可能如下:

在 HttpURLConnection/HttpsURLConnection 的文档中可以阅读以下内容:

每个 HttpURLConnection 实例用于发出单个请求,但 与 HTTP 服务器的底层网络连接可能是 由其他实例透明共享。

所以现在只调用close() 就可以了,但调用disconnect() 将终止其他用户的套接字/透明共享连接,然后在达到超时期限后运行到 SocketTimeOut。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-07
    • 2011-05-11
    相关资源
    最近更新 更多