[overview]
Implement a group chat tool. The client messages participating in the chat are broadcast through the server.
It mainly consists of two parts: chat server and chat client.
Chat server function overview:
1. Monitor the access and disconnection of all clients
2. When client A accesses the chat room, send the access message to other clients except client A
3. When client A exits the chat room, send the exit message to other clients except client A
4. When client A sends A message to the chat room, forward the message to other clients except client A
Chat client function overview:
1. Send message to chat server
2. Receive all messages sent by chat server
[pom dependence]
<!--netty--> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.36.Final</version> </dependency> <!--lombok rely on--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.6</version> <scope>provided</scope> </dependency>
[server initiator ChatServer]
package com.test.server; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; /** * Group chat server * */ public class ChatServer { private final int port; public ChatServer(int port) { this.port = port; } public void start() throws InterruptedException { EventLoopGroup parentGroup = new NioEventLoopGroup(); EventLoopGroup childGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(parentGroup, childGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() { protected void initChannel(SocketChannel sc) throws Exception { ChannelPipeline pipeline = sc.pipeline(); //Add a row based decoder pipeline.addLast(new LineBasedFrameDecoder(2048)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new ChatServerHandler()); } }).option(ChannelOption.SO_BACKLOG, 128).option(ChannelOption.SO_KEEPALIVE, true); ChannelFuture future = serverBootstrap.bind(port).sync(); System.out.println("Server started"); future.channel().closeFuture().sync(); } finally { parentGroup.shutdownGracefully(); childGroup.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { new ChatServer(8888).start(); } }
[ChatServerHandler]
package com.test.server; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.util.concurrent.GlobalEventExecutor; /** * Handle various situations of chat server * */ public class ChatServerHandler extends ChannelInboundHandlerAdapter { // Create a ChannelGroup,It is a thread safe collection that holds all the connections to the current server Active State Channel // GlobalEventExecutor Is a single instance, single thread EventExecutor,Is to ensure that group All of them Channel Processing // Thread is the same thread private static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); // As long as there are clients Channel If a message is sent to the current server, the execution of the method will be triggered @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // Get the channel Channel channel = ctx.channel(); // Here we want to broadcast messages to all group Clients in Channel // The message sent to oneself is different from the message sent to everyone group.forEach(ch -> { if (ch != channel) { ch.writeAndFlush(channel.remoteAddress() + ": " + msg + "\n"); } else { channel.writeAndFlush("me: " + msg + "\n"); } }); } // As long as there are clients Channel If the connection with the server is successful, this method will be executed @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // Get the current successful connection with the server channel Channel channel = ctx.channel(); System.out.println(channel.remoteAddress() + "---Go online"); group.writeAndFlush(channel.remoteAddress() + "---Go online\n"); // Will present channel Add to group in group.add(channel); } // As long as there are clients Channel Disconnect from the server and execute this method @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { // Get to the currently disconnected Channel Channel channel = ctx.channel(); System.out.println(channel.remoteAddress() + "------Offline"); group.writeAndFlush(channel.remoteAddress() + "Offline, current online population:" + group.size() + "\n"); // group It's all stored in Active State Channel,Once a certain Channel The status of is no longer Active, // group It is automatically kicked out of the collection, so the following statement does not need to be written // remove()The application scenario of the method is to Active State channel Move out group Use time // group.remove(channel); } /** * The execution of this method will be triggered when the data in the Channel is abnormal during processing * * @param ctx context * @param cause Exception object occurred * @throws Exception */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
[Client startup class ChatClient]
package com.test.client; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * Group chat client * */ public class ChatClient { private final String host; private final int port; public ChatClient(String host, int port) { this.host = host; this.port = port; } public void start() { NioEventLoopGroup group = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); try { bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LineBasedFrameDecoder(2048)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new ChatClientHandler()); } }); ChannelFuture future = bootstrap.connect(host, port).sync(); // Get keyboard input InputStreamReader is = new InputStreamReader(System.in, "UTF-8"); BufferedReader br = new BufferedReader(is); // Write the input to Channel while (true) { //br.readLine()Execute in fill()Method to get the input data. If the data is not obtained, it will block until the data is obtained future.channel().writeAndFlush(br.readLine() + "\r\n"); } } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } public static void main(String[] args) { new ChatClient("127.0.0.1", 8888).start(); } }
[ChatClientHandler]
package com.test.client; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * Client processing * */ public class ChatClientHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception { System.out.println(s); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
[operation result]
[1. Start group chat server]
[2. Start group chat client 01]
[2. Start group chat client 02]
[3. Send message test]
[4. Offline test]