Java 网络编程(Socket) - 连接异常断开情况 - Server 断网(断网测试、流程解析、原理解析)

一、引入

  1. 准备两台设备,设备 A(Android 设备)用作 Server 端,设备 B(Windows 设备)用作 Client 端

  2. Server 端与 Client 端建立 TCP 通信后,Server 端断网(异常断开),观察 Client 端的感知情况


二、测试案例

1、Server
public static final String TAG = TcpCommunicateTestClientActivity.class.getSimpleName();

private ExecutorService pool;
private static int PORT = 9999;
private ServerSocket serverSocket;
private Socket socket;
private InputStream inputStream;
private OutputStream outputStream;

private EditText etContent;
private Button btnSend;
pool = new ThreadPoolExecutor(2, 3, 10, TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());

pool.execute(() -> {
    try {
        serverSocket = new ServerSocket(PORT);
        Log.i(TAG, "Server 在 " + PORT + " 端口等待连接");
        socket = serverSocket.accept();
        Log.i(TAG, "Server 得到连接");

        inputStream = socket.getInputStream();
        outputStream = socket.getOutputStream();

        byte[] dataArr = new byte[100];
        int length;
        while ((length = inputStream.read(dataArr)) != -1) {
            String res = new String(dataArr, 0, length);
            Log.i(TAG, "Server 接收:" + res);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
});

etContent = findViewById(R.id.et_content);
btnSend = findViewById(R.id.btn_send);

btnSend.setOnClickListener(v -> {
    String content = etContent.getText().toString();
    pool.execute(() -> {
        try {
            outputStream.write(content.getBytes());
            Log.i(TAG, "Server 发送:" + content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    });
});
2、Client
public class TcpCommunicateTestClient {

    private static String IP = "127.0.0.1";
    private static int PORT = 9999;

    public static void main(String[] args) throws IOException {
        Socket socket = new Socket();
        socket.connect(new InetSocketAddress(IP, PORT));
        System.out.println("Client 连接成功");

        InputStream inputStream = socket.getInputStream();
        OutputStream outputStream = socket.getOutputStream();

        new Thread(() -> {
            try {
                byte[] dataArr = new byte[100];
                int length;
                while ((length = inputStream.read(dataArr)) != -1) {
                    String res = new String(dataArr, 0, length);
                    System.out.println("Client 接收:" + res);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        Scanner scanner = new Scanner(System.in);
        while (true) {
            String str = scanner.next();
            System.out.println("Client 发送:" + str);
            outputStream.write(str.getBytes());
        }
    }
}

三、断网(异常断开)测试流程

  1. Server 端启动
# Server 端输出内容

Server 在 9999 端口等待连接
  1. Client 端启动
# Server 端输出内容

Server 得到连接
# Client 端输出内容

Client 连接成功
  1. Server 端断网,此时,Client 端还无法感知连接断开
# Server 端输出内容

java.net.SocketException: Software caused connection abort
	发生在【while ((length = inputStream.read(dataArr)) != -1) {】
  1. Client 端发送一些内容,稍后,读取位置抛出异常,Client 端感知连接断开
# Client 端输出内容

Client 发送:123
java.net.SocketException: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。
	发生在【while ((length = inputStream.read(dataArr)) != -1) {】
  1. Client 端再发送一些内容,发送位置抛出异常
# Client 端输出内容

Client 发送:123
Exception in thread "main" java.net.SocketException: Connection reset by peer
	发生在【outputStream.write(str.getBytes());】

四、流程解析

  1. Client 端与 Server 端成功建立 TCP 连接

  2. Server 端断网,但 Client 端的 TCP 连接仍然处于半开状态

  3. Client 端的 inputStream.read() 一直阻塞,等待 Server 端发送数据

  4. 当 Client 端尝试发送数据时,操作系统发现无法将数据送达,触发 TCP 重传机制

  5. 经过多次重传后,操作系统认为连接已经断开,Socket 的状态被标记为异常

  6. 此时,Client 端的 inputStream.read() 抛出 SocketException 异常

  7. 此时,当 Client 端再次尝试发送数据时,outputStream.write() 也会抛出 SocketException 异常


五、原理解析

1、TCP 通信半开状态
  1. 在 TCP 通信中,Server 断网,Client 端无法立刻感知

  2. TCP 通信本身没有提供实时检测连接状态的机制

  3. 此时,Client 端进入半开状态

  4. 在半开状态下,Client 端的操作系统仍然认为连接是有效的,因为它没有收到 Server 端的任何关闭信号

2、Client 端 inputStream.read() 阻塞
  1. Client 端 inputStream.read() 是一个阻塞操作,它会一直等待,直到有数据从 Server 端发送过来

  2. 如果 Server 端断网,Client 端 inputStream.read() 会一直阻塞

  3. 因为此时 TCP 连接仍然被认为是有效的,操作系统不会主动通知 Client 端连接已经断开

3、重传机制
  1. Client 端尝试发送数据时,如果 Server 端断网,操作系统会发现无法将数据送达,触发 TCP 的重传机制

  2. 在经过多次重传仍然无法成功发送数据后,操作系统会认为连接已经断开,并触发 java.net.SocketException 异常

  3. 此时,Socket 的状态会被标记为异常

4、Client 端 inputStream.read()outputStream.write() 异常抛出
  1. outputStream 与 inputStream 是同一个 Socket 的输入输出流

  2. 它们都依赖于底层的 Socket 连接状态,任何一方的异常都会影响另一方

  3. 当 Socket 的状态被标记为异常时,inputStream.read() 会立即抛出异常,而不是继续阻塞

  4. 如果此时 Client 端再次尝试发送数据时,outputStream.write() 也会抛出 SocketException 异常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值