IO/NIO interactive simulation and progressive implementation

IO

IO Server

public class SocketServer {
    public static void main(String[] args) {
        //The server number corresponds to the client number, and the advantages and disadvantages are noted on the server side.
        //server1();
        //server2();
        server3();
    }

    /**
     *Disadvantages of server1:
     * 1. The accept() method blocks the thread and cannot continue processing until the client connects (one client connection corresponds to one server processing thread)
     * 2. When the client and server complete an interaction, the program ends
     * 3. The stream closing code is relatively bloated, and the content of BufferedWriter needs to be added at the end with '\\
'
     * 4. The interactive content is hard-coded and cannot be input through the console.
     * 5. Fixed port number, fixed IP, readLine to read a line, etc.
     */
    public static void server1() {
        ServerSocket serverSocket = null;
        BufferedReader bufferedReader = null;
        BufferedWriter bufferedWriter = null;
        try {
            serverSocket = new ServerSocket(8999);
            //Block until client connection is successful
            Socket socket = serverSocket.accept();

            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //readLine will block
            String s = bufferedReader.readLine();
            System.out.println("The server received the message from the client:" + s);

            bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bufferedWriter.write("The server sends a message to the client\\
");
            bufferedWriter.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //Each try catch must be written separately. Aggregation into one will cause a stream closing exception and cannot enter the closing process of other streams.
            if(serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            if(bufferedWriter != null) {
                try {
                    bufferedWriter.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            //Only need to close the outer buffer, and its close method internally closes the incoming stream
            //If you do not close it and just wait for the jvm to finally come to an end, it will occupy resources for an uncertain period of time (if it is file reading and writing, it will occupy the file descriptor fd)
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }


    /**
     * server2 advantages:
     * 1. The server and client can interact any number of times and send messages continuously.
     * 2. Optimized the writing method of stream closing
     *
     *Disadvantages of server2:
     * 1. The accept() method blocks the thread and cannot continue processing until the client connects (one client connection corresponds to one server processing thread)
     * 2. cpu idling
     * 3. IP fixed, etc.
     * 4. A server can only interact with one client
     */
    public static void server2() {
        System.out.println("Please enter the port number and wait for the client to connect...");
        try (BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
             ServerSocket serverSocket = new ServerSocket(Integer.valueOf(consoleReader.readLine()));
             Socket socket = serverSocket.accept();
             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
        ){
            System.out.println("The client is connected, starting to interact with the client...");
            //This loop ensures that multiple messages can be sent or received continuously
            while (true) {
                if (bufferedReader.ready()) {
                    System.out.println("Received message from client:" + bufferedReader.readLine());
                }

                if (consoleReader.ready()) {
                    printWriter.println(consoleReader.readLine());
                    printWriter.flush();
                }
            }
            //Can only send once and receive once
// while (true) {
// System.out.println("Received message from client:" + bufferedReader.readLine());
//
// printWriter.println(consoleReader.readLine());
// printWriter.flush();
// }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * server3 advantages:
     * A server can interact with a client
     * Added thread pool
     *
     *Disadvantages of server3:
     * 1. The accept() method blocks the thread and cannot continue processing until the client connects (one client connection corresponds to one server processing thread)
     * 2. cpu idling
     * 3. IP fixed, etc.
     * 4. If multiple clients are opened, because System.in is shared by the server, after the server sends a message, the client will randomly receive individual messages.
     */
    public static void server3() {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(0, Runtime.getRuntime().availableProcessors() * 2,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());
        System.out.println("Please enter the port number and wait for the client to connect...");
        try (BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
             ServerSocket serverSocket = new ServerSocket(Integer.valueOf(consoleReader.readLine()));
        ){
            //This loop ensures multiple client connections
            while (true) {
                Thread.sleep(1000);
                Socket socket = serverSocket.accept();
                System.out.println("The client is connected, starting to interact with the client...");

                pool.submit(new Thread(()->{
                    try(
                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                        PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
                    ) {
                        while (true) {
                            Thread.sleep(1000);
                            if(bufferedReader.ready()) {
                                System.out.println("Received message from client" + Thread.currentThread() + ":" + bufferedReader.readLine());
                            }

                            if (consoleReader.ready()) {
                                printWriter.println(consoleReader.readLine());
                                printWriter.flush();
                            }
                        }
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    } finally {
                        if(socket != null) {
                            try {
                                socket.close();
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

IO Client

public class SocketClient {
    public static void main(String[] args) {
        //client1();
        //client2();
        client3();
    }

    public static void client1() {
        Socket socket = null;
        BufferedReader bufferedReader = null;
        BufferedWriter bufferedWriter = null;
        try {
            socket = new Socket("127.0.0.1", 8999);

            bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //The newline character is essential, otherwise the server readLine() cannot determine that a line has been written.
            bufferedWriter.write("The client sends a message to the server\\
");
            bufferedWriter.flush();

            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String s = bufferedReader.readLine();
            System.out.println("The client received the message from the server:" + s);

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if(socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            if(bufferedWriter != null) {
                try {
                    bufferedWriter.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }


    public static void client2() {
        System.out.println("Please enter the port number:");
        try (
                BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
                Socket socket = new Socket("127.0.0.1", Integer.valueOf(consoleReader.readLine()));
                PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        ){
            System.out.println("Connection successful, start interacting with the server");
            //Can send or receive multiple messages continuously
            while (true) {
                Thread.sleep(1000);
                if (bufferedReader.ready()) {
                    System.out.println("Received message from server:" + bufferedReader.readLine());
                }

                if (consoleReader.ready()) {
                    printWriter.println(consoleReader.readLine());
                    printWriter.flush();
                }
            }
            //Can only send once and receive once
// while (true) {
// printWriter.println(consoleReader.readLine());
// printWriter.flush();
//
// System.out.println("Received message from server:" + bufferedReader.readLine());
// }

        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static void client3() {
        System.out.println("Please enter the port number:");
        try (
                BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
                Socket socket = new Socket("127.0.0.1", Integer.valueOf(consoleReader.readLine()));
                PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        ){
            System.out.println("Connection successful, start interacting with the server");
            //Can send or receive multiple messages continuously
            while (true) {
                if (bufferedReader.ready()) {
                    System.out.println("Received message from server:" + bufferedReader.readLine());
                }

                if (consoleReader.ready()) {
                    printWriter.println(consoleReader.readLine());
                    printWriter.flush();
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

NIO

NIO Server

public class SocketServer {
    public static void main(String[] args) {
        //server1();
        //server2();
        //server3();
        //server4();
        server5();
    }


    /**
     * Advantages: NIO non-blocking mode, connections will not be blocked, reading and writing will not be blocked
     * shortcoming:
     * 1. When the client and server complete an interaction, the program ends
     * 2. The interactive content is hard-coded and cannot be entered through the console.
     * 3. cpu idling
     * 4. The selector multiplexer is not used, and its programming is actually similar to the BIO form (the server still corresponds to one client for each thread)
     */
    private static void server1() {

        try (
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        ){
            serverSocketChannel.socket().bind(new InetSocketAddress(9999));
            serverSocketChannel.configureBlocking(false);
            //accept is set to non-blocking, so it needs to be placed in a loop, otherwise the program will end directly
            while (true) {
                Thread.sleep(1000);
                SocketChannel socketChannel = serverSocketChannel.accept();
                if(socketChannel != null) {
                    System.out.println("The client is connected, starting to interact with the client...");
                    socketChannel.configureBlocking(false);
                    ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                    socketChannel.read(readBuffer);
                    readBuffer.clear();

                    System.out.println("Received client message: " + new String(readBuffer.array(), "utf-8").trim());

                    ByteBuffer writeBuffer = ByteBuffer.wrap("Send message to client".getBytes());
                    socketChannel.write(writeBuffer);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * Improvements:
     * 1. Support one-to-one interaction between client and server, multiple messages
     * shortcoming:
     * 1. One client is not supported for multiple servers.
     * 2. If multiple clients are opened, because System.in is shared by the server, after the server sends a message, the client will randomly receive individual messages.
     * 3. cpu idling
     * 4. The selector multiplexer is not used, and its programming is actually similar to the BIO form (the server still corresponds to one client for each thread)
     */
    private static void server2() {
        try (
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
        ){
            serverSocketChannel.socket().bind(new InetSocketAddress(9999));
            serverSocketChannel.configureBlocking(false);
            //accept is set to non-blocking, so it needs to be placed in a loop, otherwise the program will end directly
            while (true) {
                Thread.sleep(1000);
                SocketChannel socketChannel = serverSocketChannel.accept();
                if(socketChannel != null) {
                    System.out.println("The client is connected, starting to interact with the client...");
                    socketChannel.configureBlocking(false);
                    ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                    while (true) {
                        if(socketChannel.read(readBuffer) > 0) {
                            System.out.println("Received client message: " + new String(readBuffer.array(), "utf-8").trim());
                            readBuffer.clear();
                        }

                        if (consoleReader.ready()) {
                            ByteBuffer writeBuffer = ByteBuffer.wrap(consoleReader.readLine().getBytes());
                            socketChannel.write(writeBuffer);
                            writeBuffer.clear();
                        }
                        Thread.sleep(1000);
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Improvements:
     * 1. Support one client to multiple servers
     * shortcoming:
     * 1. cpu idling
     * 2. The selector multiplexer is not used, and its programming is actually similar to the BIO form (the server still corresponds to one client for each thread)
     */
    private static void server3() {
        try (
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
        ) {
            serverSocketChannel.socket().bind(new InetSocketAddress(9999));
            serverSocketChannel.configureBlocking(false);
            List<SocketChannel> socketChannels = new ArrayList<>();
            //accept is set to non-blocking, so it needs to be placed in a loop, otherwise the program will end directly
            while (true) {
                Thread.sleep(1000);
                boolean ready = consoleReader.ready();
                byte[] bytes = null;
                if (ready) {
                    bytes = consoleReader.readLine().getBytes();
                }
                //Call accept once and it will become null unless there is a new client connection.
                SocketChannel socketChannel = serverSocketChannel.accept();
                if (socketChannel != null) {
                    socketChannels.add(socketChannel);
                    socketChannel.configureBlocking(false);
                    System.out.println("The client is connected, starting to interact with the client...");
                }
                for (SocketChannel channel : socketChannels) {
                    ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                    if (channel.read(readBuffer) > 0) {
                        System.out.println("Received client message: " + new String(readBuffer.array(), "utf-8").trim());
                        readBuffer.clear();
                    }

                    if (ready) {
                        ByteBuffer writeBuffer = ByteBuffer.wrap(bytes);
                        channel.write(writeBuffer);
                        writeBuffer.clear();
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * Improvements:
     * 1. Support one client to multiple servers (the server can send messages, the client can receive them, and all clients can send messages and the server can receive them)
     * 2. Compared with the server3 multi-threading method, the server only needs to start one main thread to interact with all clients.
     * shortcoming:
     * 1. cpu idling
     * 2. The selector multiplexer is not used, and its programming is actually similar to the BIO form (the server still corresponds to one client for each thread)
     */
    static final List<SocketChannel> socketChannels4 = new CopyOnWriteArrayList<>();
    private static void server4() {
        try (
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
        ) {
            serverSocketChannel.socket().bind(new InetSocketAddress(9999));
            serverSocketChannel.configureBlocking(false);
            //accept is set to non-blocking, so it needs to be placed in a loop, otherwise the program will end directly
            while (true) {
                SocketChannel socketChannel = serverSocketChannel.accept();
                if (socketChannel != null) {
                    System.out.println("The client is connected, starting to interact with the client...");
                    socketChannel.configureBlocking(false);
                    socketChannels4.add(socketChannel);
                }
                Iterator<SocketChannel> iterator = socketChannels4.iterator();
                boolean ready = consoleReader.ready();
                byte[] writeBytes = null;
                if (ready) {
                    writeBytes = consoleReader.readLine().getBytes();
                }

                while (iterator.hasNext()) {
                    ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                    SocketChannel channel = iterator.next();
                    if (channel.read(readBuffer) > 0) {
                        System.out.println("Received client message: " + new String(readBuffer.array(), "utf-8").trim());
                        readBuffer.clear();
                    }

                    if (ready) {
                        ByteBuffer writeBuffer = ByteBuffer.wrap(writeBytes);
                        channel.write(writeBuffer);
                        writeBuffer.clear();
                    }
                }
                Thread.sleep(1000);
            }
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Improvements:
     * 1. Support one client to multiple servers (the server can send messages, the client can receive them, and all clients can send messages and the server can receive them)
     * 2. Compared with the server3 multi-threading method, the server only needs to start one main thread to interact with all clients.
     * shortcoming:
     * 1. cpu idling
     * 2. The selector multiplexer is not used, and its programming is actually similar to the BIO form (the server still corresponds to one client for each thread)
     */
    private static void server5() {

        try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
             Selector selector = Selector.open();
             BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
        ) {
            serverSocketChannel.socket().bind(new InetSocketAddress(9999));
            serverSocketChannel.configureBlocking(false);
            //Register serverSocketChannel to the selector and listen to the accept event
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            List<SocketChannel> socketChannels = new ArrayList<>();
            while (true) {
                //non-blocking
                selector.select(1000);
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    if(selectionKey.isAcceptable()) {
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        System.out.println("The client is connected, starting to interact with the client...");
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                        socketChannels.add(socketChannel);
                    } else if(selectionKey.isReadable()) {
                        SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
                        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                        if (socketChannel.read(readBuffer) > 0) {
                            System.out.println("Received client message: " + new String(readBuffer.array(), "utf-8").trim());
                            readBuffer.clear();
                        }
                    }
                    iterator.remove();
                }

                if(consoleReader.ready()) {
                    byte[] bytes = consoleReader.readLine().getBytes();
                    for (SocketChannel socketChannel : socketChannels) {
                        ByteBuffer writeBuffer = ByteBuffer.wrap(bytes);
                        socketChannel.write(writeBuffer);
                        writeBuffer.clear();
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

NIO Client

public class SocketClient {

    public static void main(String[] args) {
        //client1();
        //client2_3_4();
        client5();
    }

    private static void client1() {

        try (
            SocketChannel socketChannel = SocketChannel.open();
        ){
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
            while (!socketChannel.finishConnect()) {
                Thread.sleep(1000);
                System.out.println("Connecting client...");
            }

            ByteBuffer writeBuffer = ByteBuffer.wrap("Send message to server".getBytes());
            socketChannel.write(writeBuffer);
            writeBuffer.clear();

            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            while (true) {
                if(socketChannel.read(readBuffer) > 0) {
                    System.out.println("Received server message: " + new String(readBuffer.array(), "utf-8").trim());
                    readBuffer.clear();
                    break;
                }
                Thread.sleep(1000);
            }

        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }


    private static void client2_3_4() {
        try (
            SocketChannel socketChannel = SocketChannel.open();
            BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
        ){
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
            while (!socketChannel.finishConnect()) {
                Thread.sleep(1000);
                System.out.println("Connecting client...");
            }

            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            while (true) {
                if(socketChannel.read(readBuffer) > 0) {
                    System.out.println("Received server message: " + new String(readBuffer.array(), "utf-8").trim());
                    readBuffer.clear();
                }

                if (consoleReader.ready()) {
                    ByteBuffer writeBuffer = ByteBuffer.wrap(consoleReader.readLine().getBytes());
                    socketChannel.write(writeBuffer);
                    writeBuffer.clear();
                }
                Thread.sleep(1000);
            }
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private static void client5() {
        try (
            SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));
            Selector selector = Selector.open();
            BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
        ){
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_READ);
            while (true) {
                selector.select(1000);
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    if (selectionKey.isReadable()) {
                        SocketChannel channel = (SocketChannel) selectionKey.channel();
                        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                        if(channel.read(readBuffer) > 0) {
                            System.out.println("Received server message: " + new String(readBuffer.array(), "utf-8").trim());
                            readBuffer.clear();
                        }
                    }
                    iterator.remove();
                }
                if (consoleReader.ready()){
                    ByteBuffer writeBuffer = ByteBuffer.wrap(consoleReader.readLine().getBytes());
                    socketChannel.write(writeBuffer);
                    writeBuffer.clear();
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Java Skill TreeNIONIO Overview 137845 people are learning the system