monsterhxw / my-notes

技术随想
MIT License
1 stars 0 forks source link

分析 Java IO 产生 java.net.SocketException 的 Broken pipe 和 Connection reset 原因 #9

Open monsterhxw opened 2 months ago

monsterhxw commented 2 months ago

TL;DR

实验代码

RstServer 代码

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class RstServer {

    public static void main(String[] args) {
        try (ServerSocket listenSocket = new ServerSocket(9090)) {
            Socket establishedSocket = listenSocket.accept();
            System.out.println("client[" + establishedSocket + "]connect success.");

            byte[] buffer = new byte[1024];
            int len;
            while ((len = establishedSocket.getInputStream().read(buffer)) != -1) {
                System.out.println("has received from client[" + establishedSocket + "] data:" + new String(buffer, 0, len));

                String greeting = "Hello Client, I am Server[" + establishedSocket + "]";
                establishedSocket.getOutputStream().write(greeting.getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            System.out.println("Server terminated.");
        }
    }
}

Broken pipe 异常实验代码

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

public class RstBrokenPipeClient {

    public static void main(String[] args) {
        try (Socket clientSocket = new Socket()) {
            clientSocket.connect(new InetSocketAddress("127.1", 9090));

            OutputStream out = clientSocket.getOutputStream();
            System.out.println("Start to sleep for 10 seconds, please kill the server right now.");
            Thread.sleep(10_000L);

            System.out.println("Start to first write");
            // 第一次 write,客户端并不知道连接已经不在了,这次 write 不会抛异常,只会触发 RST 包,应用层是收不到的
            out.write("hello".getBytes());
            out.flush();

            Thread.sleep(2_000L);

            System.out.println("Start to second write");
            // 第二次 write, 触发 java.net.SocketException: Broken pipe (Write failed)
            out.write("world".getBytes());
            out.flush();

            System.in.read();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行结果

Start to sleep for 10 seconds, please kill the server right now.
Start to first write
Start to second write
java.net.SocketException: Broken pipe (Write failed)
    at java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
    at java.net.SocketOutputStream.write(SocketOutputStream.java:143)
    at tcprst.RstBrokenPipeClient.main(RstBrokenPipeClient.java:31)

Connection reset 异常实验代码

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class RstConnectionResetByPeerClient {

    public static void main(String[] args) {
        try (Socket clientSocket = new Socket()) {
            clientSocket.connect(new InetSocketAddress("127.1", 9090));

            OutputStream out = clientSocket.getOutputStream();
            System.out.println("Start to sleep for 10 seconds, please kill the server right now.");
            Thread.sleep(10_000L);

            System.out.println("Start to first write");
            // 第一次 write,客户端并不知道连接已经不在了,这次 write 不会抛异常,只会触发 RST 包,应用层是收不到的
            out.write("connection reset".getBytes());
            out.flush();

            Thread.sleep(2_000L);

            System.out.println("Start to second read");
            // 第二次操作「read」, 触发 java.net.SocketException: Connection reset
            byte[] buffer = new byte[1024];
            int len = clientSocket.getInputStream().read(buffer);
            System.out.println("Read data: " + new String(buffer, 0, len, StandardCharsets.UTF_8));

            System.in.read();

        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行结果

Start to sleep for 10 seconds, please kill the server right now.
Start to first write
Start to second read
java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(SocketInputStream.java:210)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at java.net.SocketInputStream.read(SocketInputStream.java:127)
    at tcprst.RstConnectionResetByPeerClient.main(RstConnectionResetByPeerClient.java:33)

Refs