java.net.SocketException: 连接被对端重置:套接字写入错误 在传输文件时发生

17

我正在尝试使用套接字实现HTTP服务器。如果客户端(例如浏览器)请求目录,则服务器会显示可用文件列表。当客户端请求文件时,问题就出现了。我收到以下错误:

java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:113)
at java.net.SocketOutputStream.write(SocketOutputStream.java:159)
at cf.charly1811.java.web.RequestHandler.writeFile(RequestHandler.java:152)
at cf.charly1811.java.web.RequestHandler.processRequest(RequestHandler.java:139)
at cf.charly1811.java.web.RequestHandler.handleRequest(RequestHandler.java:110)
at cf.charly1811.java.web.RequestHandler.run(RequestHandler.java:86)
at java.lang.Thread.run(Thread.java:745)

堆栈跟踪显示问题来自于writeFile()方法:

private void writeFile(File request) throws IOException 
{
    InputStream byteReader = new BufferedInputStream(new FileInputStream(request));
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = byteReader.read(buffer)) != -1) 
    {
        outputStream.write(buffer, 0, bytesRead);
    }
    byteReader.close();
}

但我想不出问题出在哪里。 你能帮我吗?

编辑

谢谢大家的回答。读完你们的回答后,我明白了问题出在Socket遇到错误时。以下是我的错误代码:

// Method to process a single request
handleRequest() throw IOException
{
    // process here
    // if the client request a file
    writeFile();
    // close socket when the request is processed
}

// The method is called
public run()
{
    try{
        // If an error occurs the try/catch won't be called because it is implemented outside the loop. So when an IOException occurs, the loop just stop and exit the program
        while(true)
        {
            handleRequest();
        }
    }
    catch(IOException e) {
        // Handle exception here
    }
}

我的新代码看起来像这样:

// Method to process a single request
handleRequest()
{
   try {
        // process here
        // if the client request a file
        writeFile();
        // close socket when the request is processed
    }
    // If this exception occurs the catch() method will be called
    catch(IOException e)
    {
        // handle exception here
    }
}

// The method is called
public run()
{
    while(true)
        {
            handleRequest();
        }
    }
}

1
你可能需要查看远程主机(即“客户端”)上正在发生的情况。建议:尝试使用Wireshark - FoggyDay
当出现IOException时,您的while(true)循环必须正常退出。编辑中的新代码存在一个新问题,即如果套接字异常关闭,则while循环将永远旋转。 - bond
@baconoverlord,你对这个有什么建议? - Charles-Eugene Loubao
@Charly1811,“handleRequest”和“writeFile”是同义词吗?可能是我没有完全理解你所展示的内容中缺失的要素。 - bond
writefile() 在 handleRequest() 中被调用,我在回答中忘记通知它了。抱歉 @baconoverlord - Charles-Eugene Loubao
显示剩余2条评论
3个回答

15

TCP套接字有可能处于“关闭”状态,但你的代码还未被通知。

以下是一个生命周期的动画演示。http://tcp.cs.st-andrews.ac.uk/index.shtml?page=connection_lifecycle

基本上,连接已被客户端关闭。你已经使用了 throws IOExceptionSocketException 继承自IOException,这很有效。你只需要正确处理 IOException 因为它是 API 的正常部分。

编辑:当在一个不存在或已关闭的套接字上收到包时,会发生 RST 数据包。对于你的应用程序没有区别。根据实现,reset 状态可能会保持,并且closed将永远不会正式发生。


端口可能处于“CLOSING”状态,但该状态与此问题无关。这意味着两端同时关闭,这种情况下写入操作将抛出“套接字已关闭”异常。 - user207421
1
它显示“对等方重置:套接字写入错误”。当目的地接收到发送到已关闭套接字的数据包时,会发生RST重置数据包。两端中的其中一端异常终止了连接,或者是在关闭中间发生了过快(可能在回环中),导致了这种情况。我经常看到这个错误,因此知道所有write()上的IOException都应该像连接关闭一样处理。 - bond
@EJP 这与问题有关。我试图进一步解释生命周期,而不仅仅是你的答案“连接已被对等方关闭”。错误本身说“对等方重置”。您不应该对那些每天使用套接字并在生产中看到了每种可能的中间状态条件的人进行负面评价。 - bond
你是对的 @baconoverlord。当IOException发生时,catch()方法没有被调用。通常不应该关闭套接字并停止程序。我已经更新了问题并进行修复。 - Charles-Eugene Loubao
@bond 套接字只能由拥有它的进程关闭。端口的CLOSING状态仅在应用程序执行CLOSE操作之后才会到达。您无法得知谁对您的答案进行了负评。 - user207421
3
@bond 这个链接无法打开,请你修复一下吗?谢谢。 - mljrg

1
这个问题通常是因为向已被对方关闭的连接写入数据导致的。在这种情况下,可能是用户取消了下载操作。

-1

我遇到了这个问题,但解决方法非常简单。 我正在使用1024字节缓冲区写入1 MB文件,导致出现此问题。 为了理解,在修复之前和之后请参考代码。

带异常的代码

DataOutputStream dos = new DataOutputStream(s.getOutputStream());
        FileInputStream fis = new FileInputStream(file);
        byte[] buffer = new byte[1024];
        
        while (fis.read(buffer) > 0) {
            dos.write(buffer);
        }
    

修复后:

DataOutputStream dos = new DataOutputStream(s.getOutputStream());
        FileInputStream fis = new FileInputStream(file);
        byte[] buffer = new byte[102400];
        
        while (fis.read(buffer) > 0) {
            dos.write(buffer);
        }

    

1
为什么它能工作? - Yanning Du

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