Netty architecture analysis of e-commerce system study notes

1. Review NIO

Before learning Netty, let's review the communication steps of NIO:

①Create ServerSocketChannel and configure non-blocking mode for it.

②Bind monitoring, configure TCP parameters, enter backlog size, etc.

③ Create an independent IO thread for polling the multiplexer Selector.

④Create a Selector, register the previously created ServerSocketChannel on the Selector, and set the listening flag SelectionKey.OP_ACCEPT.

⑤ Start the IO thread, execute the Selector.select() method in the loop body, and poll the ready channel.

⑥ When the channel in the ready state is polled, the operation bit needs to be judged. If it is in the ACCEPT state, it means that a new client is connected, and the accept method is called to receive the new client.

⑦ Set some parameters of the newly accessed client, such as non-blocking, and continue to register it on the Selector, set the monitoring flag, etc.

⑧ If the polled channel identification bit is READ, read it, construct a Buffer object, etc.

⑨ For more detailed problems, there is also the problem that the data is not sent and continues to be sent...

The reference code is as follows:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Vector;


public class ChatServer implements Runnable {

    //Selector
    private Selector selector;
    //Select key after registering ServerSocketChannel
    private SelectionKey serverKey;
    //Identify whether to run
    private boolean isRun;
    //List of user names in the current chat room
    private Vector<String> unames;
    //time formatter
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * Constructor
     * @param port The port number monitored by the server
     */
    public ChatServer(int port) {
        isRun = true;
        unames = new Vector<String>();
        init(port);
    }

    /**
     * Initialize selector and server socket
     *
     * @param port The port number monitored by the server
     */
    private void init(int port) {
        try {
            // get the selector instance
            selector = Selector.open();
            // get server socket instance
            ServerSocketChannel serverChannel = ServerSocketChannel.open();
            // bind port number
            serverChannel.socket().bind(new InetSocketAddress(port));
            // set to non-blocking
            serverChannel.configureBlocking(false);
            //Register the ServerSocketChannel to the selector and specify its behavior as "waiting to accept a connection"
            serverKey = serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            printInfo("server starting...");
        } catch (IOException e) {
            e.printStackTrace ();
        }

    }

    @Override
    public void run() {
        try {
            // poll the selector to select the key
            while (isRun) {
                //Select the key of a group of channels that are ready for IO operation. When it is equal to 1, it means that there is such a key
                int n = selector.select();
                if (n > 0) {
                    //Get the set of selected keys from the selector and iterate
                    Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                    while (iter.hasNext()) {
                        SelectionKey key = iter.next();
                        //If the channel of this key is waiting to accept a new socket connection
                        if (key.isAcceptable()) {
                            //Remember to remove this key, otherwise new connections will be blocked and cannot connect to the server
                            iter.remove();
                            //Get the channel corresponding to the key
                            ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                            //Accept new connections and return the socket channel to the client peer
                            SocketChannel channel = serverChannel.accept();
                            if (channel == null) {
                                continue;
                            }
                            // set to non-blocking
                            channel.configureBlocking(false);
                            // Register this socket channel with the selector, specifying its behavior as "read"
                            channel.register(selector, SelectionKey.OP_READ);
                        }
                        //If the behavior of this key's channel is "read"
                        if (key.isReadable()) {
                            readMsg(key);
                        }
                        //If the behavior of the channel of the key is "write"
                        if (key.isWritable()) {
                            writeMsg(key);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace ();
        }
    }

    /**
     * Read data from the socket channel corresponding to the key
     * @param key selection key
     * @throws IOException
     */
    private void readMsg(SelectionKey key) throws IOException {
        //Get the socket channel corresponding to this key
        SocketChannel channel = (SocketChannel) key.channel();
        //Create a buffer of size 1024k
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        StringBuffer sb = new StringBuffer();
        //Read the data of the channel into the buffer
        int count = channel.read(buffer);
        if (count > 0) {
            //Flip the buffer area (change the buffer area from write data mode to read data mode)
            buffer.flip();
            // Convert the data in the buffer area to String
            sb.append(new String(buffer.array(), 0, count));
        }
        String str = sb.toString();
        //If there is "open_" in the message, it means that the client is ready to enter the chat interface
        //The data format passed by the client is "open_zing", which means that the user named zing requests to open the chat window
        //If the user name list is updated, the user name data should be written to each connected client
        if (str.indexOf("open_") != -1) {//The client connects to the server
            String name = str.substring(5);
            printInfo(name + " online");
            unames.add(name);
            //Get the key selected by the selector and iterate
            Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
            while (iter.hasNext()) {
                SelectionKey selKey = iter.next();
                //If it is not the key of the server socket channel, set the data to this key
                //And update the action that this key is interested in
                if (selKey != serverKey) {
                    selKey.attach(unames);
                    selKey.interestOps(selKey.interestOps() | SelectionKey.OP_WRITE);
                }
            }
        } else if (str.indexOf("exit_") != -1) {// client sends exit command
            String uname = str.substring(5);
            // delete this username
            unames.remove(uname);
            //Append "close" string to key
            key.attach("close");
            //Update the action that this key is interested in
            key.interestOps(SelectionKey.OP_WRITE);
            // Get the selected key on the selector and iterate
            //Append the updated namelist data to each socket channel key, and reset the operation that the key is interested in
            Iterator<SelectionKey> iter = key.selector().selectedKeys().iterator();
            while (iter.hasNext()) {
                SelectionKey selKey = iter.next();
                if (selKey != serverKey && selKey != key) {
                    selKey.attach(unames);
                    selKey.interestOps(selKey.interestOps() | SelectionKey.OP_WRITE);
                }
            }
            printInfo(uname + " offline");
        } else {// Read client chat message
            String uname = str.substring(0, str.indexOf("^"));
            String msg = str.substring(str.indexOf("^") + 1);
            printInfo("("+uname+")说:" + msg);
            String dateTime = sdf.format(new Date());
            String smsg = uname + " " + dateTime + "\n  " + msg + "\n";
            Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
            while (iter.hasNext()) {
                SelectionKey selKey = iter.next();
                if (selKey != serverKey) {
                    selKey.attach(smsg);
                    selKey.interestOps(selKey.interestOps() | SelectionKey.OP_WRITE);
                }
            }
        }
    }

    /**
     * Write data to the socket channel corresponding to the key
     * @param key
     * @throws IOException
     */
    private void writeMsg(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        Object obj = key.attachment();
        //Here it is necessary to set the additional data of the key to be empty, otherwise there will be problems
        key.attach("");
        //The additional value is "close", then cancel the key and close the corresponding channel
        if (obj.toString().equals("close")) {
            key.cancel();
            channel.socket().close();
            channel.close();
            return;
        }else {
            // write data to the channel
            channel.write(ByteBuffer.wrap(obj.toString().getBytes()));
        }
        //Reset this key interest
        key.interestOps(SelectionKey.OP_READ);
    }

    private void printInfo(String str) {
        System.out.println("[" + sdf.format(new Date()) + "] -> " + str);
    }

    public static void main(String[] args) {
        ChatServer server = new ChatServer(19999);
        new Thread(server).start();
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325392128&siteId=291194637