Summary of nio error prone knowledge points and code practice

Posted by mattdarnold on Mon, 31 Jan 2022 22:04:21 +0100

Summary of error prone knowledge points

  1. NIO operates based on channel and buffer. Data is always read from the channel to the buffer or written from the buffer to the channel. The selector is used to listen to the events of multiple channels (such as connection request, data arrival, etc.), so a single thread can listen to multiple client channels

  2. Relation diagram of selector, channel and buffer


    Relationship description:
    1. Each channel corresponds to a buffer
    2. A selector corresponds to a thread. A selector can listen to multiple channels (connections)
    3. The event determines which channel the program switches to according to the listener of the selector
    4. The selector will switch on each channel according to different events monitored
    5.buffer is a memory block with an array at the bottom
    6. The buffer in NiO can be read or written, but it needs to be switched by flip and other methods
    7.channel is bidirectional and can read and write at the same time. It can read and write data asynchronously. It can read data from buffer or write data to buffer

  3. ServerSocketChannel and SocketChannel are used for TCP data reading and writing.

  4. Functions of ServerSocketChannel and SocketChannel: a simple understanding of ServerSocketChannel is that when a client initiates a connection request to the server, the server allocates a SocketChannel to the client through ServerSocketChannel, and then the communication between the client and the server depends on this SocketChannel. Whether it is the client or the server, the channel needs to be connected through the buffer.


    Each client has a SocketChannel to communicate with the server Read (buffer), write the contents in the channel into the buffer (read the contents from the channel), channel Write (buffer), write the contents of the buffer to the channel (write the contents to the channel).

  5. Selector: the selector can detect whether an event occurs on multiple registered channels (Note: multiple channels can be registered to the same selector in the form of events). If an event occurs, it will obtain the event and then process each event accordingly. In this way, only one single thread can manage multiple channels, that is, manage multiple connections and requests.

  6. OP_ The read event is triggered not only when it is readable, but also when the data in the channel is read, the other end of the connection pipe is closed, there is an error pending, and the other party sends a message.

Case 1:

Simple server-side and client-side connection (console display)
Server side code:

package com.haust.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class server {
    public static void main(String[] args) throws IOException {
        //Create ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //Get a selector object
        Selector selector = Selector.open();
        //Bind a local 6666 port and listen on the server
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));
        //Set channel to non blocking
        serverSocketChannel.configureBlocking(false);
        //Register serverSocketChannel with the selector, and the monitored event is OP_ACCEPT
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true){
            //Here, we wait for 1 second. If no event occurs, we return
            if (selector.select(1000) == 0){//No event occurred within 1 second
                System.out.println("The server waits for 1 second and there is no connection");
                continue;
            }
            //If the returned is > 0, the relevant selectionKey collection is obtained
            //selector.keys() indicates the number of channel s registered on the selector
            //1. If the returned > 0, it indicates that the event of interest has been obtained
            //2.selector.selectedKeys() returns the collection of events listened to
            //Reverse channel acquisition through selectionKeys
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            //Iterate over the selectionset, where we use the < selectionset >
            Iterator<SelectionKey> keyIterator = selectionKeys.iterator();

            while (keyIterator.hasNext()){
                //Get Selectionkey
                SelectionKey key = keyIterator.next();
                //Handle the events according to the channel corresponding to the key
                if (key.isAcceptable()){//The accept event indicates a new client connection
                    //Generate a socketChannel for the client
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //Set to non blocking
                    socketChannel.configureBlocking(false);
                    //Register the socketChannel with the selector, and the listening event is OP_READ, and associate a buffer to the socketChannel at the same time
                    socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }
                if (key.isReadable()){
                    //Get the channel in reverse through the key
                    SocketChannel channel = (SocketChannel)key.channel();
                    //Get the buffer associated with the channel
                    ByteBuffer buffer = (ByteBuffer)key.attachment();
                    //Read data
                    channel.read(buffer);
                    System.out.println("from client"+new String(buffer.array()));
                }
                //Manually move the current selectionkey from the collection to prevent repeated operations
                keyIterator.remove();
            }


        }
    }
}

Client code

package com.haust.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class client {
    public static void main(String[] args) throws IOException {
        //Get a network channel
        SocketChannel socketChannel = SocketChannel.open();
        //Set non blocking
        socketChannel.configureBlocking(false);
        //Provide server-side IP and port
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);
        //Connect server
        if (!socketChannel.connect(inetSocketAddress)){
            while (!socketChannel.finishConnect()){
                System.out.println("Because the connection takes time, the client will not block and can do other work..");
            }
        }
        //If the connection is successful, send the data
        String str = "hello nio";

        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
        //Send data and write buffer data to channel
        socketChannel.write(buffer);
        System.in.read();


    }
}

effect:

Case 2: group chat system

Simple server-side and client-side group chat system (console board)
Server side code:

package com.haust.groupchat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

public class GroupChatServer {
    //Define attributes
    private Selector selector;
    private ServerSocketChannel listenChannel;
    private static final int PORT =6667;
    //Constructor initialization
    public GroupChatServer(){
        try {
            //Get selector
            selector = Selector.open();
            //ServerSocketChannel
            listenChannel = ServerSocketChannel.open();
            //Binding port
            listenChannel.socket().bind(new InetSocketAddress(PORT));
            //Set non blocking mode
            listenChannel.configureBlocking(false);
            //Register the listenChannel with the selector
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void listen(){
        try {
            while (true){
                int count = selector.select(2000);//The select method itself is blocked (without parameters). 2000 represents the result returned after 2 seconds and becomes non blocking
                if (count>0){//Indicates that an event has occurred
                    //Traversal to get selectionKey set
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()){
                        //Remove the selectionkey
                        SelectionKey key = iterator.next();
                        //Listen to accept
                        if (key.isAcceptable()){
                            SocketChannel sc = listenChannel.accept();
                            //Register the sc with the selector
                            sc.configureBlocking(false);
                            sc.register(selector,SelectionKey.OP_READ);
                            //Tips
                            System.out.println(sc.getRemoteAddress()+"go online");
                        }
                        if (key.isReadable()){
                            readData(key);
                        }
                        //Prevent repeated operations
                        iterator.remove();
                    }
                }else {
//                    System.out.println("wait...);
                }
            }
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            //Handling in case of exception
        }
    }
    //Read client messages
    public void readData(SelectionKey key){
        SocketChannel channel = null;
        try {
            //Get channel
            channel = (SocketChannel) key.channel();
            //Create buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int count = channel.read(buffer);
            //Perform corresponding processing according to the value of count
            if (count>0){
                //Convert the data in the cache into a string
                String msg = new String(buffer.array());
                //Output this message
                System.out.println("from client"+msg);

                //Forward messages to other clients (remove yourself)
                sendInfoToOtherClients(msg,channel);
            }

        }catch (IOException e){
            try {
                System.out.println(channel.getRemoteAddress()+"Offline");
                //Unregister
                key.cancel();
                //Close channel
                channel.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
    //Method of forwarding message to other clients
    private void sendInfoToOtherClients(String msg,SocketChannel self) throws IOException {
        System.out.println("Server forwarding message");
        //Traverse all socketchannels registered on the selector and exclude self
        for (SelectionKey key : selector.keys()) {
            //Get the corresponding SocketChannel through the key
            Channel targetChannel = key.channel();
            //Exclude yourself
            if (targetChannel instanceof SocketChannel && targetChannel != self){
                //transformation
                SocketChannel dest = (SocketChannel)targetChannel;
                //Store msg in buffer
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                //Write buffer data to channel
                dest.write(buffer);
            }
        }
    }


    public static void main(String[] args) {
        //Create server object
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }

}

Client code

package com.haust.groupchat;



import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;

public class GroupChatClient {
    //Define related attributes
    private final String HOST = "127.0.0.1";//IP address of the server
    private final int PORT = 6667;//Server port
    private Selector selector;
    private SocketChannel socketChannel;
    private String username;

    //Constructor to complete initialization
    public GroupChatClient() throws IOException {
        selector = Selector.open();
        //Connect server
        socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1",PORT ));
        //Set non blocking
        socketChannel.configureBlocking(false);
        //Register channel with selector
        socketChannel.register(selector, SelectionKey.OP_READ);
        //Get username
        username = socketChannel.getLocalAddress().toString().substring(1);

        System.out.println(username+"is OK");
    }
    //Send message to server
    public void sendInfo(String info){
        info = username + " say:" +info;
        try {
            socketChannel.write(ByteBuffer.wrap(info.getBytes()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void readInfo(){
        try {
            int readChannels = selector.select(2000);
            if (readChannels > 0){//Indicates that there are channels available
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    if (key.isReadable()){
                        //Get the relevant channel
                        SocketChannel sc = (SocketChannel)key.channel();
                        //Get a buffer
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        //read
                        sc.read(buffer);
                        //Convert the data of the read buffer into a string
                        String msg = new String(buffer.array());
                        System.out.println(msg.trim());
                        //Delete the current selectionkey to prevent repeated operations
                        iterator.remove();

                    } else{
//                        System.out.println("no channel temporarily");
                    }

                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        //Start client
        final GroupChatClient chatClient = new GroupChatClient();
        //Start a thread to read the data sent from the server every three seconds
        new Thread(){
            public void run(){
                while (true){
                    chatClient.readInfo();
                    try{
                        Thread.currentThread().sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        //Send data to server
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()){
            String s = scanner.nextLine();
            chatClient.sendInfo(s);
        }

    }
}

Special note: the channel registered with the selector is non blocking! After the listening event is processed, the key corresponding to this listening event needs to be removed to prevent repeated execution. (if not removed, it will be in the collection next time)
If the same program is run multiple times (different threads are applied), it needs to be set in the idea


effect:




Disconnect the three clients

Topics: Java Netty network NIO