Netty: NIO Selector selector (detailed notes and source code of C/S demo)

Netty foundation java NIO

Selector

Three elements: Selector, SelectableChannel, selectable channel and SelectionKey

In essence, the Selector is a listener that monitors whether the channel is generated by operations we care about. The operations correspond to events (connect, receive, read / write). The SelectionKey is used to represent specific events. When the channel is selectable, the channel is registered in the Selector. At this time, the Selector maintains the correlation between the channel and events.

Selector, which manages the set of registered channels and their status SelectableChannel is an abstract class that provides the api that channels can be selected and implemented. FileChannel is not selectable. All Socket related channels are selectable Can a channel be registered on multiple selectors? tolerable Multiple channels can be registered on a selector, but a channel can only be registered once in a selector

SelectionKey encapsulates the events to be monitored, including connection, receiving, reading and writing. On the one hand, the Selector cares about which events the channel will handle On the other hand, when an event is triggered, which events will the channel handle

Use NIO to realize the communication between server and clinet (without selector)

Server

public static void main(String[] args) throws Exception {  
//Create a server channel and use open to obtain
        ServerSocketChannel ServerChannel = ServerSocketChannel.open();
        //Set ip and port number
        SocketAddress address = new InetSocketAddress("127.0.0.1", 4321);
        //socket bound to service channel
        ServerChannel.socket().bind(address);

        //Waiting for client connection
        SocketChannel socketChannel = ServerChannel.accept();

        //    The data is processed through the buffer
        ByteBuffer writebuffer = ByteBuffer.allocate(128);
        writebuffer.put("hello client i am server".getBytes());
        writebuffer.flip();
        socketChannel.write(writebuffer);

        //    Read data from client
        ByteBuffer readbuffer = ByteBuffer.allocate(128);
        StringBuffer stringBuffer = new StringBuffer();
        socketChannel.read(readbuffer);
        readbuffer.flip();
        while (readbuffer.hasRemaining()) {
            stringBuffer.append((char) readbuffer.get());
        }
        System.out.println("client data :" + stringBuffer.toString());

        socketChannel.close();
        ServerChannel.close();
}

client

  public static void main(String[] args) throws Exception {
        //Open a socket channel
        SocketChannel socketChannel = SocketChannel.open();
        //Set ip and port number
        SocketAddress address = new InetSocketAddress("127.0.0.1", 4321);
        //Connect to this address
        socketChannel.connect(address);

        //    Write before read
        //    The data is processed through the buffer
        ByteBuffer writebuffer = ByteBuffer.allocate(128);
        writebuffer.put("hello server i am client ".getBytes());
        writebuffer.flip();
        socketChannel.write(writebuffer);

        //    Read data from client
        ByteBuffer readbuffer = ByteBuffer.allocate(128);
        StringBuffer stringBuffer = new StringBuffer();
        socketChannel.read(readbuffer);
        readbuffer.flip();
        while (readbuffer.hasRemaining()) {
            stringBuffer.append((char) readbuffer.get());
        }
        System.out.println("server data :" + stringBuffer.toString());
        socketChannel.close();
    }

We don't use a selector here. Next, we write a Nio selector server to compare and learn

Nio selector server

In SelectionKey, we often judge several operation types

  • isAcceptable(): Connect
  • isConnectable(): ready
  • isReadable(): read
  • isWritable(): write

Code execution and writing details: see notes

public static void main(String[] args) throws Exception {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //Set ip and port number
        SocketAddress address = new InetSocketAddress("127.0.0.1", 4321);
        //socket bound to service channel
        serverSocketChannel.socket().bind(address);

        //     Set this channel to non blocking
        serverSocketChannel.configureBlocking(false);
        //    Open a selector
        Selector selector = Selector.open();
        //    Register the channel to the selector where the selector is declared to listen for events
        //    Generally speaking, we listen for connection events
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        /*
         * Manage channels through selectors
         * Need to know whether the monitored channel is triggered by an event
         * When the return value of the select method is > 0, it indicates how many operations need to be processed
         * So we need to keep polling whether it has time to process
         * */
        while (true) {
            int ready = selector.select();
            if (ready == 0) {
                continue;
            }
            //    Get the operation collection through the Selected keys
            Set<SelectionKey> set = selector.selectedKeys();
            Iterator<SelectionKey> iterator = set.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                //    In order to avoid duplication of operations, we process one and remove one
                iterator.remove();
                //We use the method in the key to determine what operation to handle
                /*
                 * isAcceptable() : connect
                 * isConnectable() : be ready
                 * isReadable() : read
                 * isWritable() : write in
                 * */
                if (key.isAcceptable()) {
                    //Handling accpt events
                    //Get the client connection and register the write event
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    //   After processing the connection status, subsequent write operations are added to the selection
                    socketChannel.register(selector, SelectionKey.OP_WRITE);
                } else if (key.isWritable()) {
                    //    After processing the connection, we can use the key to obtain the corresponding event channel
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    //Handling write events
                    ByteBuffer writebuffer = ByteBuffer.allocate(128);
                    writebuffer.put("hello client i am server from 4321".getBytes());
                    writebuffer.flip();
                    socketChannel.write(writebuffer);
                    //We can register the next events in this channel through the key
                    key.interestOps(SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    //Handling read events
                    SocketChannel socketChannel = (SocketChannel) key.channel();

                    //    Read data from client
                    ByteBuffer readbuffer = ByteBuffer.allocate(128);
                    //Read data
                    int read = socketChannel.read(readbuffer);
                    //read = -1 means you have finished reading
                    if (read == -1) {
                        key.cancel();
                    }
                    //Refresh buffer
                    readbuffer.flip();
                    //Use string buffer to splice the read data
                    StringBuffer stringBuffer = new StringBuffer();
                    while (readbuffer.hasRemaining()) {
                        stringBuffer.append((char) readbuffer.get());
                    }
                    System.out.println("client data :" + stringBuffer.toString());


                } else if (key.isConnectable()) {

                }
            }

        }

    }

[usage]

a. First, obtain the channel through the open method and set the channel to non blocking b. Obtain the selector through the open method, register the channel into the selector, and set the event to be processed by the channel (OP_ACCEPT) c. Polling selector, is there any operation to process currently? Select() > 0? If so, to get the Set of operations to be processed and traverse When traversing the SelectionKey, judge the corresponding operation, and set different processing methods for different operations Such as OP_ACCEPT, receive and register the client channel, and listen for subsequent events, such as OP_WRITE Such as OP_WRITE: obtain the channel itself through the key method, read the data and continue to listen to events, such as OP_READ

Posted by e11rof on Tue, 16 Nov 2021 18:55:42 -0800