java.net.SocketException: 连接重置

184

我在尝试从套接字读取时遇到以下错误。我正在对该InputStream执行readInt()操作,但是出现了这个错误。查阅文档后发现,连接的客户端部分关闭了连接。在这种情况下,我是服务器。

我可以访问客户端日志文件,它没有关闭连接,并且其日志文件表明我正在关闭连接。那么有人知道为什么会发生这种情况吗?还有什么其他要检查的?当本地资源可能达到阈值时是否会出现这种情况?


我注意到我有以下这行代码:

socket.setSoTimeout(10000);

就在readInt()之前。有一个原因(长故事),但只是好奇,是否存在导致指定错误的情况?我在我的IDE中运行服务器,并且碰巧将我的IDE卡在断点上,然后我注意到我的日志中开始出现完全相同的错误。

总之,只是提一下,希望不是一个错误的提示。


2
你有双方的堆栈跟踪吗?你能更详细地描述一下网络架构吗?(是通过公共互联网传输还是在同一台机器上?还是介于两者之间?)这种情况是否一直发生?还是间歇性的? - Stu Thompson
请参见 https://dev59.com/duo6XIcBkEYKwwoYTzNK。 - Raedwald
我在使用WAMP时遇到了同样的问题,但通过在远程服务器上工作来解决了它。 - Florian-Martin
一般来说,我的问题是,SQL密码过期会导致这个问题吗? - Sm Srikanth
17个回答

146

可能有几种原因。

  1. 另一端故意以我不会在此说明的方式重置了连接。对于应用软件来说,这是罕见且通常不正确的做法,但商业软件并非未知之事。

  2. 更常见的原因是向已经正常关闭的连接写入数据。换句话说,这是应用程序协议错误。

  3. 当套接字接收缓冲区中仍有未读数据时关闭套接字也可能导致此问题。

  4. 在Windows中,“软件导致连接中止”(与“连接重置”不同)是由您的端发送网络问题引起的。Microsoft有一篇知识库文章介绍了这个问题。


@MattLyons 谢谢。有比那篇更好的MSDN文章。坦白地说,我觉得那篇很难相信。在正确的源和目标IP地址建立之前,连接甚至都不存在。我看过的MSDN文章提到持久网络错误会超时连接。 - user207421
1
鉴于您提到的前三点,这个链接可能会在十年后对您有所帮助:https://www.edureka.co/community/7308/how-does-java-net-socketexception-connection-reset-happen#:~:text=More%20commonly%2C%20it%20is%20caused,in%20the%20socket%20receive%20buffer. - zanderwar
1
@zanderwar 谢谢。那里充斥着抄袭现象。 - user207421
这个答案也出现在这里http://net-informations.com/java/err/reset.htm - Alan Telles
@AlanTelles 平时一样,抄袭者改写时总是会引入错误。我并没有说端口没有打开,这不会导致异常;关于套接字被关闭的部分也是错误的。 - user207421

70

连接重置是指收到了TCP RST。这种情况发生在您的对等方接收到无法处理的数据时,可能有多种原因。

最简单的情况是当您关闭套接字(socket)后,在输出流上写入更多数据。通过关闭套接字,您告诉对等方您已经结束通话,并且它可以忘记您的连接。但是,当您在该流上发送更多数据时,对等方会拒绝并用RST通知您它不再监听。

另外,在某些情况下,作为中介的防火墙甚至远程主机本身可能会“忘记”您的TCP连接。如果长时间不发送任何数据(2小时是常见的超时时间),或者因为对等方重新启动并且丢失其关于活动连接的信息,则可能会发生这种情况。在这些失效的连接中发送数据也会导致RST。


根据额外的信息更新:

仔细检查您处理SocketTimeoutException的方式。如果在套接字操作被阻塞时超过了配置的超时时间,则会引发此异常。当抛出此异常时,套接字本身的状态不会改变,但是如果您的异常处理程序关闭套接字,然后尝试向其写入数据,则会导致连接重置的情况。 setSoTimeout()旨在为您提供一种干净的方式来打破可能永远阻塞的read()操作,而不必像从另一个线程中关闭套接字这样做危险的操作。


在几个方面都不正确。垃圾回收和进程退出都会导致适当的关闭,而不是重置,但是如果对等方在关闭后进行写操作,则可能会引发重置而不是EOS。只有读取器设置了读取超时,才会引发SocketTimeoutExceptions异常。 - user207421
并不总是意味着已经收到了RST信号。也有可能是由这一端生成的。 - user207421
你不能向已关闭的套接字写入数据。这将引发“SocketException: socket closed”异常,并且对端不会收到RST信号。该答案完全错误。 - user207421

21

每当我遇到这样的问题时,我通常会使用像WireShark这样的工具,查看来回传递的原始数据。你可能会惊讶于在哪些地方出现了断开连接,只有在尝试读取时才会收到通知。


16

你应该非常仔细地检查完整的跟踪记录,

我有一个服务器套接字应用程序,并解决了java.net.SocketException:连接重置的问题。

在我的情况下,它发生在从关闭其连接的客户端 Socket 对象读取时,原因可能是网络中断、防火墙或应用程序崩溃或意外关闭。

实际上,当我从此 Socket 对象读取时遇到错误时,我正在重新建立连接。

Socket clientSocket = ServerSocket.accept();
is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
int readed = is.read(); // WHERE ERROR STARTS !!!

有趣的是,对于我的JAVA Socket,如果客户端连接到我的ServerSocket并关闭其连接而不发送任何内容,则会重复调用is.read()。看起来由于在无限循环中从此套接字读取数据,您尝试从已关闭的连接读取数据。 如果您使用以下代码进行读取操作;

while(true)
{
  Receive();
}

然后你会得到一个类似下面的堆栈跟踪信息

java.net.SocketException: Socket is closed
    at java.net.ServerSocket.accept(ServerSocket.java:494)

我所做的就是关闭ServerSocket,重新建立连接并等待进一步的客户端连接。

String Receive() throws Exception
{
try {                   
            int readed = is.read();
           ....
}catch(Exception e)
{
        tryReConnect();
        logit(); //etc
}


//...
}

这可以重新建立我的连接,以解决未知客户端套接字的丢失问题。

private void tryReConnect()
        {
            try
            {
                ServerSocket.close();
                //empty my old lost connection and let it get by garbage col. immediately 
                clientSocket=null;
                System.gc();
                //Wait a new client Socket connection and address this to my local variable
                clientSocket= ServerSocket.accept(); // Waiting for another Connection
                System.out.println("Connection established...");
            }catch (Exception e) {
                String message="ReConnect not successful "+e.getMessage();
                logit();//etc...
            }
        }

由于您可以从下面的图像看到,如果没有使用try和catch, 您无法判断连接是否中断,因为一切似乎都是正确的,我无法找到其他方法。 我在不断收到Connection reset的同时获取了这个快照。

输入图像描述


@MarquisofLorne 是的,没错,这就是为什么那个例程在另一个 while 循环中的原因。 - Davut Gürbüz
@MarquisofLorne,我注意到在阅读我的答案后需要改为“正在被重复调用”。感谢你指出这一点,5年前:)。现在我已经进行了改进,基于阅读-1、超时异常等。不优雅的连接中断仍然是一个问题,有不同的人们遵循的不同的不活动管理方法。 - Davut Gürbüz
为什么你关闭了服务器套接字?这一切都毫无道理。 - undefined
@user207421 给它一个新的开始。在我的设置中,有很多网络层从边到边,有时候当一端看到ESTABLISHED时,另一端观察到SYNC_SENT。在这种情况下,我们会重新启动整个应用程序。此外,如果线程在accept时被阻塞,它会被捕获并重新尝试,以全新的状态再次开始。这是我从7年前记得的。谢谢您的友善评论。 - undefined

12

有些难为情,但当我遇到这个问题时,简单地说是我在读完所有数据之前关闭了连接。对于返回小字符串的情况,它可能能够正常工作,但这可能是因为整个响应已经被缓冲,然后我关闭了它。

对于返回大量文本的情况,由于返回的内容超过了缓冲区的大小,就会抛出异常。

您可能要检查这种疏忽。请记住,打开URL就像打开文件一样,确保在完全读取后关闭它(释放连接)。


我遇到了这个问题。原来是webClient在程序早期被初始化并且从未重置,导致其不堪重负。在每次调用时重新初始化它解决了我的问题。 - Chidozie Nnachor

7

我遇到了同样的错误。现在我已经找到了解决方法。问题是客户端程序在服务器读取流之前就结束了。


如果使用 System.exit(0) 来终止程序,而没有人调用 socket.close() 来关闭连接,那么连接就会处于不良状态且未被正确关闭。更准确地说,他有一个客户端程序,在没有关闭套接字的情况下关闭了程序,这是一个不好的事情,应该进行修复。 - Dean Hiller
1
@DeanHiller 不会的。操作系统会以应用程序本来应该关闭的方式关闭套接字。 - user207421
@EJP……我不确定……我只知道我们可以用System.exit来重现它,但我不记得那时的操作系统/配置……在服务器上调用socket.close()可以防止连接重置,并且它的行为更加正常。 - Dean Hiller
2
@DeanHiller 读取进程在写入进程完成写入之前退出。 - user207421
是的!我在客户端程序末尾添加了sleep(5000),发现服务器端在5秒后出现了连接重置异常。这意味着我的客户端程序在服务器程序之前关闭,导致了套接字异常。 - Umer Farooq
显示剩余2条评论

6

我曾经遇到过一个使用Java编写的SOA系统的问题。我将客户端和服务器运行在不同的物理机器上,它们长时间以来一直正常工作,但是当客户端日志中出现了连接重置时,在服务器日志中并没有发现任何异常。即使重新启动客户端和服务器也无法解决这个问题。最终我们发现服务器端的堆栈已经非常满了,因此我们增加了JVM可用的内存:问题解决了!请注意,日志中没有OutOfMemoryError:内存只是紧缺,而不是耗尽。


1
问题中没有提到关于 OutOfMemoryError 的内容。 - user207421
1
是的,我知道,我没有说过其他的话。 - Pino

3
检查您的服务器的Java版本。我所遇到的问题是因为我的Weblogic 10.3.6使用的JDK版本是1.7.0_75,而该REST端点在TLSv1.2以下的协议下运行时会关闭。默认情况下,Weblogic会尝试协商最强共享协议。有关详细信息,请参见此处:Issues with setting https.protocols System Property for HTTPS connections
我添加了详细的SSL日志记录来识别支持的TLS版本。这表明握手时使用的是TLSv1。
-Djavax.net.debug=ssl:handshake:verbose:keymanager:trustmanager -Djava.security.debug=access:stack 我通过将该功能推出到我们兼容JDK8的产品中来解决了这个问题,因为JDK8默认使用TLSv1.2。对于仍受限于JDK7的用户,我也成功地测试了一个解决方案,即升级到TLSv1.2。我使用了这个答案:How to enable TLS 1.2 in Java 7

非常感谢,你向我展示了解决问题的新方向。最终我发现这就是问题所在!!! - karl li
SSL协议不匹配不会导致连接重置,它们会导致握手失败。 - user207421

1
我也曾遇到一个Java程序试图通过SSH连接到服务器发送命令的问题。问题出在执行Java代码的机器上,它没有连接到远程服务器的权限。write()方法正常运行,但read()方法会抛出java.net.SocketException: Connection reset异常。我通过将客户端SSH密钥添加到远程服务器已知密钥中来解决了这个问题。

1
我也遇到了HTTP 500错误“java.net.SocketException: Connection reset”,经过几天的分析后发现问题是由AWS NAT Gateway引起的。希望这能帮助别人节省时间并解决问题。
基础设施: AWS API Gateway(HTTP),AWS CloudMap,AWS Fargate。
问题调查: 经过深入调查,发现问题是由AWS NAT Gateway引起的,它会保留特定NAT转换的信息350秒,并在此期间没有流量时将其删除。 我的应用程序使用Java HTTP客户端,它会保持连接20分钟。因此,在350秒后的第1或2个请求结束时,会出现HTTP 500 Connection Reset。NAT Gateway没有关于该特定转换的信息,所以它会以RST标志响应-这就是为什么我在日志中看到“Connection Reset”的原因。
实施的解决方案: 将HTTP Java客户端更改为HTTP Apache客户端。 HTTP Apache客户端可以正确地保持连接活动。 您还可以选择调整本机Java HTTP客户端以发送TCP keep-alive,或者在350秒之前关闭非活动连接。

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