三次握手四次挥手

256 阅读3分钟

BIO

客户端

package org.example.net.bio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;
import java.util.Scanner;

public class BioClient {
    public static void main(String[] args) throws IOException {
        int port = 8888;
        String ip = "127.0.0.1";
        Socket socket = new Socket(ip, port);
        // socket.setSoTimeout(10 * 1000);
        BufferedReader in = null;
        PrintWriter out = null;
        System.out.println("客户端启动!");
        try {
            Scanner scanner = new Scanner(System.in);
            while (true) {
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                out = new PrintWriter(socket.getOutputStream(), true);
                String input = scanner.nextLine();
                out.println(input);
                if (input.equals("exit")) break;
                System.out.println("输入完毕,等待回应");
                String data = in.readLine();
                System.out.println("输入完毕,有回应了");
                System.out.println(new Date() + "客户端收到服务端响应内容:" + data);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

服务端

package org.example.net.bio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.Scanner;

public class BioServer {
    public static void main(String[] args) throws IOException {
        int port = 8888;
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("服务器启动!");
        while (true) {
            /*
            try {
                TimeUnit.MINUTES.sleep(1);
                System.out.println("服务端从sleep中唤醒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            */
            Socket socket = serverSocket.accept();
            System.out.println("服务器收到连接了");
            new Thread(new SocketHandle(socket)).start();
        }
    }
}

class SocketHandle implements Runnable {
    Socket socket;


    SocketHandle(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        Scanner scanner = new Scanner(System.in);
        try {
            while (true) {
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                out = new PrintWriter(socket.getOutputStream(), true);
                String data = in.readLine();
                if (data.equals("exit")) {
                    break;
                }
                System.out.println(new Date() + "服务端收到客户端的消息:" + data);

                System.out.println("服务端接收完毕,准备发送消息");
                out.println(scanner.nextLine());
                System.out.println("服务端接收完毕,发送消息完毕");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("当前线程结束了");
    }
}

测试场景

  • 正常执行效果 客户端发送两次数据,服务端一次返回,最后客户端发送exit,服务端读取后,close连接,然后客户端close连接。 image.png

  • 如果服务端accept之前一直sleep呢,在accept之前,连接状态是什么样的? 在accept之前,连接是建立状态。在accept之前,这个"连接"会放在服务端操作系统的全连接队列中。当sleep之后,服务端可以消费该连接,然后正常处理该连接上的读写操作。

  • 如果"连接"在服务端的全连接队列中,此时客户端已经可以发送数据,那么服务端会返回ACK吗?

服务端可以返回ACK,这个ACK是操作系统的TCP/IP协议栈返回的。

  • 如果这个数据发送成功了,由于服务端还没有accept连接,服务端无法处理这个请求,客户端等待服务端响应超时,客户端主动关闭了连接,服务端还能正常的消费全连接队列中的这个连接吗?

不影响服务端消费,因为这个连接已经在全连接队列中了。操作系统收到客户端的FIN之后,没有从全连接队列中删除。

  • 如果消费成功了,那么服务端还能读取那个已经响应了ACK的请求数据吗?如果能读取成功,处理完这个请求数据之后,要返回结果的话,能写数据成功吗?

服务端可以正常读取这个数据,但是写响应时,客户端的操作系统收到这个响应数据后,会被RST掉。

image.png

image.png

上图中,虽然服务端在accept之前处于sleep状态,但是客户端依然可以三次握手成功,依然可以发送数据成功。客户端读取服务端数据超时之后,关闭连接,发送FIN。服务端accept连接之后,可以读取数据,并发送响应,但是被客户端RST。