什么可能导致套接字ConnectException:连接超时?

时间:2010-10-06 23:13:09

标签: java sockets exception https httpurlconnection

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

我的本​​地计算机和位于办公室的测试服务器上的一切都运行良好,但我遇到了一个非常非常奇怪的问题,这个问题只发生在我们的生产和登台服务器上(偶尔也会发生)。我知道这些服务器和我们办公室的服务器之间的主要区别在于它们位于其他地方,与它们的客户端 - 服务器通信速度相当慢,但在此之前它在生产中也能很好地工作。

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

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

请注意,这不是SocketTimeoutExceptionconnect()上的HttpURLConnection方法表示如果在建立连接之前超时到期,则会抛出conn.getResponseCode()方法。此外,当发生这种情况时,我可以致电EOFException,我的响应代码为200。

  • 在服务器端,ObjectInputStream的构造函数中会抛出OutputStream,它会尝试读取序列化头但由于客户端永远不会得到要写入的HttpsURLConnection而失败。

如果它有帮助,这里是在调用getOutputStream()之前在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

问题是,我不知道这是怎么发生的,特别是考虑到它只发生偶尔(没有明确的活动模式,我可以告诉),即便如此只有在那里(相对)客户端和服务器之间的高延迟。

鉴于到目前为止我能够找到关于{{1}}的内容,我想知道我们的服务器运行在网络上是不是网络或防火墙问题...但是这不是鉴于请求显然已经通过servlet,我觉得很有意义。此外,在同一网络上运行的其他应用程序尚未报告类似问题。

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

2 个答案:

答案 0 :(得分:9)

我们遇到过与您类似的情况。通常在高负荷下并且在测试时不易重现。还没有解决它,但这是我们经历的步骤。

如果是防火墙问题,我们会收到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%

答案 1 :(得分:1)

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

  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中。 / p>