Python中文网

一个关于 编程问题的解答网站.

有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java什么可能导致socket连接异常:连接超时?

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

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

不管怎样,现在发生的事情如下:

  • 在设置选项(如读取超时)和属性(如HttpURLConnection上的Content-Type)后,客户机调用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)

请注意,这不是一个SocketTimeoutException,而HttpURLConnection上的connect()方法表示,如果超时在建立连接之前过期,它会抛出这个SocketTimeoutException。而且,当这种情况发生时,我可以调用conn.getResponseCode(),得到200的响应代码

  • 在服务器端,EOFExceptionObjectInputStream的构造函数中抛出一个EOFException,该构造函数试图读取序列化头,但失败了,因为客户端从未获得要写入的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实现的。此外,在同一网络上运行的其他应用程序也没有报告类似问题

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


共 (2) 个答案

  1. # 1 楼答案

    我们在一个与你类似的案例中遇到了这些。通常在高负载下,不容易在测试中重现。我们还没有修复,但这是我们所经历的步骤

    如果是防火墙问题,我们会得到连接被拒绝或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()

    Returns the error stream if the connection failed but the server sent useful data nonetheless. If the connection was not connected, or if the server did not have an error while connecting or if the server had an error but no error data was sent, this method will return null.

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

    更新:从今天起,我们学会了面对这个问题,因为我们将每天400000个请求中的失败率合计为200-300个,即0.00075%

  2. # 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的文档中,可以阅读以下内容:

    Each HttpURLConnection instance is used to make a single request but the underlying network connection to the HTTP server may be transparently shared by other instances.

    因此,现在只调用close()就可以了,但同时调用disconnect()将终止其他用户/透明共享连接的套接字,在达到超时时间后,这些连接将运行到SocketTimeOut