Netty group chat system based on netty analysis

Keywords: Netty

Preface

We explained the basic use of Netty earlier. This time, we will use Netty to complete a group chat system to realize simple data communication between the server and the client;

Basic requirements of group chat system:

  • The server can monitor the client online, offline, and forward the client message;
  • The client can send messages to other users in groups or receive messages sent by other customers;

Code example

Server:

public class GroupChatServer {

    private String host;
    private int port;

    public GroupChatServer(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128) // Set the number of connections in the thread queue
                    .childOption(ChannelOption.SO_KEEPALIVE, true) // Set keep active connection state
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // Get pipeline
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            // Add decoder
                            pipeline.addLast(new StringDecoder());
                            // Add encoder
                            pipeline.addLast(new StringEncoder());
                            // Add custom business processing handler
                            pipeline.addLast(new GroupChatServerHandler());
                        }
                    });

            System.out.println("Netty Group chat server started~");
            ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().sync();

        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }


    public static void main(String[] args) throws Exception {
        new GroupChatServer("127.0.0.1", 8899).start();
    }
}

Server custom handler:

public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> {

    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * Connection establishment event
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();

        String msg = channel.remoteAddress() + "Joined the chat~";
        channelGroup.writeAndFlush(msg);

        // Add channel to channelGroup
        channelGroup.add(channel);
    }

    /**
     * Disconnect event
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();

        String msg = "[Server]: " + channel.remoteAddress() + "Offline.~";
        channelGroup.writeAndFlush(msg);
    }

    /**
     * channel Handling activity status events
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        System.out.println(simpleDateFormat.format(new Date()) + "/" + ctx.channel().remoteAddress() + "Online.~");
    }

    /**
     * channel Inactive
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(simpleDateFormat.format(new Date()) + "/" + ctx.channel().remoteAddress() + " Offline~");
    }

    /**
     * Read data event
     *
     * @param ctx
     * @param s
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(simpleDateFormat.format(new Date()) + "/" + "Server received from[" + channel.remoteAddress() + "]Message for[" + s + "]");
        System.out.println("The server starts forwarding messages...");
        for (Channel ch : channelGroup) {
            if (channel != ch) {
                ch.writeAndFlush(channel.remoteAddress() + "Said:" + s);
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

Client:

public class GroupChatClient {
    private String serverHost;
    private int serverPort;

    public GroupChatClient(String serverHost, int serverPort) {
        this.serverHost = serverHost;
        this.serverPort = serverPort;
    }

    public void start() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            // Add decoder
                            pipeline.addLast(new StringDecoder());
                            // Add encoder
                            pipeline.addLast(new StringEncoder());
                            // Add custom business processing handler
                            pipeline.addLast(new GroupChatClientHandler());
                        }
                    });


            ChannelFuture channelFuture = bootstrap.connect(serverHost, serverPort).sync();
            Channel channel = channelFuture.channel();
            System.out.println("---" + channel.localAddress() + "---");

            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()) {
                String msg = scanner.nextLine();
                channel.writeAndFlush(msg);
            }
        } finally {
            group.shutdownGracefully();
        }

    }

    public static void main(String[] args) throws Exception {

        new GroupChatClient("127.0.0.1", 8899).start();
    }

}

Client custom handler:

public class GroupChatClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
        // Print message output
        System.out.println(s);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

Communication Demonstration:
Server:

Netty group chat server started~
2020-02-04 12:22:11 / / 127.0.0.1:4099 go online~
2020-02-04 12:22:14 / / 127.0.0.1:4141 go online~
2020-02-04 12:22:24 / the server receives a message from [/ 127.0.0.1:4099], the content is [I am 4099]
The server starts forwarding messages...
2020-02-04 12:22:31 / the server received a message from [[127.0.0.1:4141] with the content of [I am 4141]
The server starts forwarding messages...

client1:

---/127.0.0.1:4099---
/127.0.0.1:4141 join chat~
I am 4099.
/127.0.0.1:4141 say: I'm 4141

client2:

---/127.0.0.1:4141---
/127.0.0.1:4099 say: I am 4099
 I am 4141.
97 original articles published, praised 16, visited 10000+
Private letter follow

Posted by ryeman98 on Tue, 04 Feb 2020 10:16:41 -0800