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.