Tong Gong said that Java NIO of netty series makes group chat possible (I am addicted to chatting with myself)

Keywords: Java Programming Netty

Hello, I am Tong Ge, this is the fourth article in the netty series.

Welcome to my public Tong Tong to read Source Systematically to learn the knowledge of Source-Architecture.

brief introduction

In the previous chapter, we learned the story of BIO/NIO/AIO in Java together. This chapter will take you all together to use pure NIO to implement a group chat system that gets more and more addicted to chatting.

Business Logic Analysis

First, let's analyze the functions of group chat:

(1) Join group chats and notify others;

(2) Speak and inform others;

(3) Quit group chat and notify others;

A simple group chat system has nearly three functions. To record user information conveniently, a user ID is automatically assigned to the user when he joins the group chat.

Business implementation

Top Code:

// This is an internal class
private static class ChatHolder {
    // We've only used one thread, and normal AshMap is fine
    static final Map<SocketChannel, String> USER_MAP = new ConcurrentHashMap<>();

    /**
     * Join group chat
     * @param socketChannel
     */
    static void join(SocketChannel socketChannel) {
        // Assign an id to someone when they join. This article comes from the public slave Tong Gong Read Source Code.
        String userId = "user"+ ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
        send(socketChannel, "Your id For:" + userId + "\n\r");

        for (SocketChannel channel : USER_MAP.keySet()) {
            send(channel, userId + " Join group chat" + "\n\r");
        }

        // Add current user to map
        USER_MAP.put(socketChannel, userId);
    }

    /**
     * Exit Group Chat
     * @param socketChannel
     */
    static void quit(SocketChannel socketChannel) {
        String userId = USER_MAP.get(socketChannel);
        send(socketChannel, "You quit group chat" + "\n\r");
        USER_MAP.remove(socketChannel);

        for (SocketChannel channel : USER_MAP.keySet()) {
            if (channel != socketChannel) {
                send(channel, userId + " Quit group chat" + "\n\r");
            }
        }
    }

    /**
     * Diffuse your words
     * @param socketChannel
     * @param content
     */
    public static void propagate(SocketChannel socketChannel, String content) {
        String userId = USER_MAP.get(socketChannel);
        for (SocketChannel channel : USER_MAP.keySet()) {
            if (channel != socketChannel) {
                send(channel, userId + ": " + content + "\n\r");
            }
        }
    }

    /**
     * send message
     * @param socketChannel
     * @param msg
     */
    static void send(SocketChannel socketChannel, String msg) {
        try {
            ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
            writeBuffer.put(msg.getBytes());
            writeBuffer.flip();
            socketChannel.write(writeBuffer);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Service-side code

The service-side code uses the NIO implementation of the previous chapter directly, but here you need to insert the business logic implemented above into the appropriate events in a timely manner.

(1) The accept event, when the connection is established, indicates that you joined the group chat;

(2) read events, when reading data, indicate that someone has spoken;

(3) When the connection is disconnected, the group chat is quit;

OK, go directly to the code. To distinguish it from the code in the previous chapter, Tong Gou deliberately added some tags:

public class ChatServer {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8080));
        serverSocketChannel.configureBlocking(false);
        // Bind accept events to selector
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // Blocked on select
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            // Traversing selectKeys
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                // If it is an accept event
                if (selectionKey.isAcceptable()) {
                    ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
                    SocketChannel socketChannel = ssc.accept();
                    System.out.println("accept new conn: " + socketChannel.getRemoteAddress());
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    // Join group chat, this article comes from "Tong Ge Read Source" of Gongcong number
                    ChatHolder.join(socketChannel);
                } else if (selectionKey.isReadable()) {
                    // If it is a read event
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    // Read data into buffer
                    int length = socketChannel.read(buffer);
                    if (length > 0) {
                        buffer.flip();
                        byte[] bytes = new byte[buffer.remaining()];
                        // Read data into byte array
                        buffer.get(bytes);

                        // Line breaks follow messages
                        String content = new String(bytes, "UTF-8").replace("\r\n", "");
                        if (content.equalsIgnoreCase("quit")) {
                            // Quit group chat, this article comes from "Tong Ge Read Source" of Gongcong number
                            ChatHolder.quit(socketChannel);
                            selectionKey.cancel();
                            socketChannel.close();
                        } else {
                            // Diffusion, this article comes from the public slave "Tong Ge Read Source"
                            ChatHolder.propagate(socketChannel, content);
                        }
                    }
                }
                iterator.remove();
            }
        }
    }
}

test

Open four XSHELL clients, connect telnet 127.0.0.1 8080, and then start group chatting.

Tong Gong found that chatting with himself was addictive too. He couldn't stop at all. I'll talk to myself again for a while ^^

summary

This article Tong Gong along with you to implement the "group chat system", remove comments will also be about 100 lines of code, is it very simple?That's the magic of NIO web programming. I find writing web programming addictive too ^^

problem

Neither of these chapters uses NIO to implement clients. Do you know how?

Tip: The server needs to listen for accept events, so it needs to have a ServerSocketChannel, and the client is directly connected to the server, so it is OK to use SocketChannel directly, a SocketChannel is equivalent to a Connection.

Finally, I would like to welcome you to read Source Systematically and learn the knowledge of Source-Architecture in Tong Tong.

Posted by hellonoko on Tue, 19 Nov 2019 10:50:46 -0800