JAVA IO from zero practice (10. NIO communication to achieve multiplexing practice)

Introduced the three core Buffer Selector Channels of NIO before, and did a simple practice,
This time we will combine them all to realize the communication under NIO. You can compare this process with the previous communication under BIO.

Communication practice under NIO

Server:

/**
 * Communication implementation under NIO non-blocking channel: server
 * */
public class Server {<!-- -->
    public static void main(String[] args) throws IOException {<!-- -->
        // get channel
        ServerSocketChannel ssChannel = ServerSocketChannel. open();
        //Switch to non-blocking mode
        ssChannel. configureBlocking(false);
        //bind connection port
        ssChannel.bind(new InetSocketAddress(9999));
        // get selector
        Selector selector = Selector. open();
        // Bind the channel to the selector and specify the listening event
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("Server started on port 9999");

        //The selector starts to poll whether there is an event that is ready for data transmission
        while (selector. select()>0){<!-- -->
            //Get all ready events
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();

            while (it.hasNext()) {<!-- -->
                SelectionKey selectionKey = it. next();
                //Judge what this event is
                if(selectionKey.isAcceptable()){<!-- -->
                    System.out.println("The detected event is a connection event");
                    SocketChannel channel = ssChannel. accept();
                    channel. configureBlocking(false);
                    channel.register(selector,SelectionKey.OP_READ);
                }else if (selectionKey.isReadable()){<!-- -->
                    System.out.println("The detected event is a data read and write event");
                    SocketChannel clientChannel = (SocketChannel) selectionKey. channel();
                    ByteBuffer buffer = ByteBuffer. allocate(1024);
                    int len;
                    while ((len = clientChannel.read(buffer))>0){<!-- -->
                        buffer. flip();
                        System.out.println(new String(buffer.array(),0,len));
                        buffer. clear();
                    }
                }
                it. remove();
            }
        }
    }
}


This code is not very easy for novices to understand. In order to take care of novices, let’s explain it in detail:

First here:

 ServerSocketChannel ssChannel = ServerSocketChannel. open();
        //Switch to non-blocking mode
        ssChannel. configureBlocking(false);
        //bind connection port
        ssChannel.bind(new InetSocketAddress(9999));
        // get selector
        Selector selector = Selector. open();
        // Bind the channel to the selector and specify the listening event
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);

We first create a channel designated as non-blocking mode, and then the port listens to 9999, and we register this channel on the selector with SelectionKey.OP_ACCEPT mode.

Why use OP_ACCEPT here, because this mode is to monitor connections, and the selector is used to monitor whether there is a client accessing port 9999.

ok Now the selector enters the polling state in the while loop:

Like a patrolling soldier, it keeps checking for any requests:

 //The selector starts to poll whether there is an event that is ready for data transmission
        while (selector. select()>0){<!-- -->
            //Get all ready events
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();

            while (it.hasNext()) {<!-- -->
                SelectionKey selectionKey = it. next();
                //Judge what this event is

When a request comes in it gets all the requests that come up. Then process one by one, judge one by one, what kind of request is coming?

First determine whether it is a data connection request:

 if(selectionKey.isAcceptable()){<!-- -->
                    SocketChannel channel = ssChannel. accept();
                    channel. configureBlocking(false);
                    channel.register(selector,SelectionKey.OP_READ);

If it is a connection request, it will create a channel for this connection request, and register this channel to the selector in the mode of reading data. Note that just accept is a connection request, and this time it is read, which is the actual data transmission mode.

In this way, one request creates a channel, and another request creates another channel.
This enables multiple client access.

So if it is a data connection request, it will enter here:
Get the channel of this data connection from selectionKey.
Then you can read data from the channel normally through the buffer. very similar to the previous ones

else if (selectionKey.isReadable()){<!-- -->
                    SocketChannel channel = (SocketChannel) selectionKey. channel();

                    ByteBuffer buffer = ByteBuffer. allocate(1024);

                    int len;

                    while ((len = channel.read(buffer))>0){<!-- -->
                        buffer. flip();
                        System.out.println(new String(buffer.array(),0,len));
                        buffer. clear();
                    }

                }

In the end, no matter what kind of request it is, it will be removed after processing, and the selector will continue to process new requests:

 it. remove();

This is the core idea of multiplexing. Similar ideas are used in many applications, using one thread to handle multiple communication requests, such as tomcat, redis, nginx

client

The client code is very simple
Get a channel between us, just write in it directly

public class Client {<!-- -->
    public static void main(String[] args) throws IOException {<!-- -->
        // get channel
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9999));

        socketChannel. configureBlocking(false);

        ByteBuffer buffer = ByteBuffer. allocate(1024);

        Scanner scanner = new Scanner(System.in);

        while (true){<!-- -->
            System.out.println("Please say:");
            String msg = scanner. nextLine();
            buffer.put(msg.getBytes());
            buffer. flip();
            socketChannel.write(buffer);
            buffer. clear();
        }

    }
}

Test Let’s open two clients and one server to send messages:

Please say:
Hello, I'm Weiwei No. 1
Please say:
121
Please say:
121
Please say:
12312415
Please say:

Please say:
Hello, I'm Weiwei No. 2
Please say:
Server started on port 9999
The event was detected as a connection event
The event was detected as a connection event
The detected event is a data read and write event
Hello, I'm Weiwei No. 1
The detected event is a data read and write event
Hello, I'm Weiwei No. 2
The detected event is a data read and write event
121
The detected event is a data read and write event
121
The detected event is a data read and write event
12312415

Process finished with exit code 130

Here is the same as explained above, the connection event is detected first, and then the read and write events are detected.

If you are a novice, it is recommended to type the above code line by line to understand its meaning repeatedly.