我的java.net.SocketException: Connection reset是什么原因引起的?

156
我们的日志中经常出现间歇性的 java.net.SocketException: Connection reset 错误。我们不确定实际发生 Connection reset 错误的原因,并且也不知道如何进行调试。
该问题似乎与我们尝试发送的消息无关。 请注意,该消息不是 connection reset by peer
您有关于这个异常的典型原因和我们应该如何处理的任何建议吗?
以下是代表性的堆栈跟踪(com.companyname.mtix.sms 是我们的组件):
java.net.SocketException: Connection reset
在java.net.SocketInputStream.read(SocketInputStream.java:168)中出现问题,
在java.io.BufferedInputStream.fill(BufferedInputStream.java:218)中填充数据时出错,
在java.io.BufferedInputStream.read(BufferedInputStream.java:235)中读取数据时出错,
在org.apache.commons.httpclient.HttpParser.readRawLine(HttpParser.java:77)中读取原始行时出错,
在org.apache.commons.httpclient.HttpParser.readLine(HttpParser.java:105)中读取行时出错,
在org.apache.commons.httpclient.HttpConnection.readLine(HttpConnection.java:1115)中读取响应状态行时出错,
在org.apache.commons.httpclient.HttpMethodBase.readStatusLine(HttpMethodBase.java:1832)中读取响应状态行时出错,
在org.apache.commons.httpclient.HttpMethodBase.readResponse(HttpMethodBase.java:1590)中读取响应时出错,
在org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:995)中执行HTTP方法时出错,
在org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:397)中执行HTTP方法时出错,
在org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:170)中执行HTTP方法时出错,
在org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:396)中执行HTTP方法时出错,
在org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:324)中执行HTTP方法时出错,
在com.companyname.mtix.sms.services.impl.message.SendTextMessage.sendTextMessage(SendTextMessage.java:127)中发送短信时出错,
在com.companyname.mtix.sms.services.MessageServiceImpl.sendTextMessage(MessageServiceImpl.java:125)中发送短信时出错,
在com.companyname.mtix.sms.services.remote.MessageServiceRemoteImpl.sendTextMessage(MessageServiceRemoteImpl.java:43)中发送短信时出错,
在sun.reflect.GeneratedMethodAccessor203.invoke(Unknown Source)中调用方法时出错,
在sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)中调用方法时出错,
在java.lang.reflect.Method.invoke(Method.java:585)中调用方法时出错,
在org.apache.axis.providers.java.RPCProvider.invokeMethod(RPCProvider.java:397)中调用方法时出错,
在org.apache.axis.providers.java.RPCProvider.processMessage(RPCProvider.java:186)中处理消息时出错,
在org.apache.axis.providers.java.JavaProvider.invoke(JavaProvider.java:323)中调用方法时出错,
在org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)中访问时出错,
在org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)中访问时出错,
在org.apache.axis.SimpleChain.invoke(SimpleChain.java:83)中调用时出错,
在org.apache.axis.handlers.soap.SOAPService.invoke(SOAPService.java:453)中调用SOAP服务时出错,
在org.apache.axis.server.AxisServer.invoke(AxisServer.java:281)中调用Axis服务器时出错,
在org.apache.axis.transport.http.AxisServlet.doPost(AxisServlet.java:699)中进行POST请求时出错,
在javax.servlet.http.HttpServlet.service(HttpServlet.java:709)中提供HTTP服务时出错,
在org.apache.axis.transport.http.AxisServletBase.service(AxisServletBase.java:327)中提供HTTP服务时出错,
在javax.servlet.http.HttpServlet.service(HttpServlet.java:802)中提供HTTP服务时出错,
在org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)中过滤请求时出错,
在org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)中过滤请求时出错,
在com.companyname.mtix.sms.http.filters.NoCacheFilter.doFilter(NoCacheFilter.java:63)中过滤请求时出错,
在org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)中过滤请求时出错,
在org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)中过滤请求时出错,
在com.companyname.mtix.sms.http.filters.MessageFilter.doFilter(MessageFilter.java:53)中过滤请求时出错,
在org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)中过滤请求时出错,
在org.apache我们的组件是一个在Tomcat下运行的Web应用程序,调用第三方Web服务发送短信,它偶然发生。异常抛出的代码行是下面代码片段中的最后一行。

我们的组件是一个 Web 应用程序,在 Tomcat 下运行,调用第三方 Web 服务发送 SMS 消息,偶尔会出现异常。代码片段中异常抛出的那一行是最后一行。

String aggregatorResponse = null;
HttpClient httpClient = prepareHttpClient( username, password );
PostMethod postMethod = preparePostMethod( textUrl );

try {
  SybaseTextMessageBuilder builder = new SybaseTextMessageBuilder();
  URL notifyUrl = buildNotificationUrl( textMessage, codeSetManager );
  String smsRequestDocument = builder.buildTextMessage( textMessage, notifyUrl );
  LOG.debug( "Sybase MT document created as: \n" + smsRequestDocument );

  postMethod.setRequestEntity( new StringRequestEntity( smsRequestDocument ) );
  LOG.debug( "commiting SMS to aggregator: " + textMessage.toString() );
  int httpStatus = httpClient.executeMethod( postMethod );

2
从完整的堆栈中可以看出,它始于“读取”操作。你的客户端已经离开,但你仍然尝试从它的流中读取。请参考我的ServerSocket解决方案:https://dev59.com/y3VD5IYBdhLWcg3wKoWH#31741436。 - Davut Gürbüz
1
对于在使用Go时遇到此问题的人,https://github.com/golang/go/issues/50984 此问题将导致“连接重置”,因为在“http.Do”完成后握手过程中多余的TLS连接被关闭。 - Kassian Sun
14个回答

70

SocketException的Javadoc说明它是

抛出以指示底层协议存在错误,例如TCP错误

在您的情况下,似乎连接已被连接的服务器端关闭。这可能是您发送请求时存在问题或其端存在问题。

为了帮助调试,您可以考虑使用像Wireshark这样的工具来查看实际的网络数据包。此外,是否有替代客户端可用于测试Web服务?如果成功,这可能表明Java代码存在错误。

由于您正在使用Commons HTTP Client,请查看Common HTTP Client Logging Guide。这将告诉您如何记录HTTP级别的请求。


5
我不相信"连接重置(Connection Reset)"意味着服务器关闭了连接(通过发送FYN标志)。我认为它意味着服务器不再响应ACK标志,或者说它不按照TCP/IP协议进行响应。如果发送了FYN标志,或者说服务器关闭了连接,你只会从read()函数中得到-1和一个EOFException异常。我计划回家后确认这一点,无论是哪种情况。 - Zombies
2
作为对Zombie评论的回应,有人知道连接重置是否意味着服务器已关闭连接吗? - James
3
根据我的经验,过去遇到类似问题的原因是服务器端网络连接不良,导致太多的连接超时。套接字超时的问题在于它的默认行为是不会超时(无限超时)。 (接下来内容请提供) - Filipe Palrinhas
22
即使是HTTPClient也没有在创建的套接字上设置默认超时时间。另一方面,服务器端所有套接字必须在几分钟后超时,否则连接将会卡住(在服务器上非常糟糕的想法)。因此,如果服务器端连接超时,客户端将收到连接重置(服务器关闭了连接),如果客户端先超时,则异常将是ConnectionTimeoutException。由于这是一个短暂的问题,解决方案是捕获和记录所有这些异常,并重新尝试连接。 - Filipe Palrinhas
@FilipePalrinhas,你说得很对,HttpClient没有在套接字连接上设置有限超时。 - asgs
@James 有几个可能的原因。请参见我在重复问题中的答案。 - user207421

54

这个错误发生在你的一侧,而不是另一侧。如果另一侧重置了连接,则异常消息应该如下所示:

java.net.SocketException reset by peer

问题的原因是HttpClient内部的连接已经过期。检查SSL的过期连接无法解决此错误。解决方案:放弃当前客户端并重新创建。


请您能详细解释一下吗?这个说法不太清楚。您所说的“dump”客户是什么意思? - undefined

18

如果您在访问部署在Glassfish3服务器上的Web服务时遇到此问题,您可能需要调整http-thread-pool设置。这样可以解决当许多并发线程调用Web服务时出现的SocketExceptions问题。

  1. 转到管理控制台
  2. 导航至“配置”->“服务器配置”->“线程池”->“http-thread-pool”。
  3. 将“最大线程池大小”设置从5更改为32
  4. 将“最小线程池大小”设置从2更改为16
  5. 重新启动Glassfish。

这也对我在Glassfish 4上有所帮助 - Dmitry

16

我也遇到了这个错误。我的问题是我使用的是支持TLS1.0的JRE6。服务器只支持TLS1.2,因此出现了此错误。


3
很难相信。你应该会收到一个“SSL握手异常(SSLHandshakeException)”。 - user207421
3
@EJP:我的回答是很久以前写的,所以无法再次检查这个问题。我只记得遇到过这个错误,然后更新到TLS 1.2就解决了这个问题。简短搜索后,我发现其他人在使用TLS 1.0并与至少支持TLS 1.2的服务器组合时也出现了 java.net.SocketException 错误,例如 https://assuresign.tenderapp.com/kb/how-to/connect-to-assuresign-using-the-tls-12-protocol。 - Jacob van Lingen
3
Java 7也会出现这种情况。默认的TLS实现是TLSv1,会在“ClientHello”中发送;如果服务器不支持该协议,则会收到“Connection reset”而不是“SSLHandshakeException”。请注意,此处仅进行翻译,不提供解释或其他额外内容。 - Dan Gravell

9
在我的情况下,这是因为我的Tomcat设置了一个不足的maxHttpHeaderSize以处理一个特别复杂的SOLR查询。
希望这能对某些人有所帮助!

5
我经常遇到这个错误,认为它是正常的。当一方试图读取时,另一方已经挂断了,因此,根据协议的不同,这可能指示问题或不构成问题。
如果我的客户端代码明确告诉服务器它要挂断,那么客户端和服务器都可以同时挂断,这条消息就不会出现。
我实现代码的方式是让客户端只是挂断而没有说再见。然后服务器可以捕获错误并忽略它。在HTTP的上下文中,我相信一级协议允许一个连接多个请求,而另一个则不允许。
因此,你可以看到其中一方潜在地会不断挂断另一方。我怀疑你收到的错误与实际操作无关,你可以简单地捕获它以防止它填满日志文件。

1
在这种情况下,协议显然是HTTP,因此除非连接的另一端退出,否则不应该发生异常情况。 - Raedwald
2
不会。即使发送方关闭,接收方仍有许多千字节的数据未读取。在这种情况下,接收方将读取所有数据,然后获得一个流结束,而不是重置。 - user207421

5

当客户端在套接字返回响应之前关闭套接字连接时,服务器端会出现此错误。在 Web 应用程序场景中,这些错误并非全部都是危险的,因为它们可以手动创建。例如,在检索响应之前退出浏览器。


3
虽然这个问题是关于客户端组件的,但你期望服务器完成交换。 - ashirley

2

这是一个旧的线程,但昨天我遇到了java.net.SocketException: Connection reset错误。

服务器端应用程序将其节流设置更改为每次只允许1个连接!因此,有时调用成功,有时不成功。我通过更改节流设置来解决问题。


1
我知道这个帖子有点老了,但我想补充一下我的看法。我们在发布后遇到了同样的“连接重置”错误。
根本原因是我们的 apache 服务器因部署而关闭。所有第三方流量都通过 apache,因为它被关闭而导致我们出现了连接重置错误。

1

异常意味着从另一端意外关闭了套接字。由于您正在调用Web服务,因此不应该发生这种情况-很可能是您发送的请求触发了Web服务中的错误。

在这些情况下,尝试记录整个请求,并查看是否注意到任何异常。否则,请联系Web服务提供商并向他们发送您记录的有问题的请求。


6
不是的。那会是‘connection reset by peer’。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接