Netty application: a simple C/S communication model

Posted by simmosn on Tue, 12 Nov 2019 17:55:15 +0100

Use Netty to realize a simple communication model. Look at the program:
1. Server side:
Class 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.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class Server {
    public void bind(int port) {
        /**
         * NioEventLoopGroup Is a multithreaded event handler for I/O operations
         * Netty Many different implementations of EventLoopGroup are provided to handle different transports
         * In this example, the server application is implemented, so two nioeventloopgroups will be used
         * One is main, also called boss, which is used to receive incoming connections
         * One is work, which is used to handle accepted connections
         * Once the boss receives the connection, it will register the information to the worker
         * How to know how many threads have been used and how to map to the created channel depends on the implementation of EventLoopGroup
         * And you can configure their relationships through constructors
         */
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            /**
             * ServerBootstrap It is a secondary startup class to start NIO
             * You can use channel directly in this service
             */
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workGroup)
                    //The NioServerSocketChannel class is specified here to illustrate how a new channel can accept incoming connections
                    .channel(NioServerSocketChannel.class)
//                    .localAddress(port)
                    /**
                     * The event handling class here is often used to handle a recently ended channel
                     * Anonymous inner class inherited from ChannelInitializer is a special processing class, whose purpose is to help users configure a new channel
                     */
                    .childHandler( new ChannelInitializer<SocketChannel>() {
                        //Add channelPipeline from serverHandler to channel
                        //Initialize each new Channel through ServerHandler
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("handler", new ServerHandler());
                            pipeline.addLast("decoder", new StringDecoder());
                            pipeline.addLast("encoder", new StringEncoder());
                        }
                    })
                    //Set request waiting queue for TCP protocol
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            //Bind port, call sync() synchronization blocking method and wait for completion
            ChannelFuture sync = bootstrap.bind(port).sync();
            System.out.println("Server listening port:"+port +" Startup success");

            //Use the sync method to block and wait for the main function to exit after the server link is closed
            sync.channel().closeFuture().sync();
        } catch (Exception e) {

        } finally {
            //Release thread pool resources
            workGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
            System.out.println("server Shut down");
        }
    }
}

ServerHandler class: this class is mainly responsible for the specific business implementation

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.net.SocketAddress;

public class ServerHandler extends SimpleChannelInboundHandler<String> {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Channel channel = ctx.channel();
        // Obtain the corresponding client ip address through the channel
        SocketAddress remoteAddress = channel.remoteAddress();
        // Get the information in the channel
        ByteBuf buf = (ByteBuf) msg;
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);

        String s1 = new String(bytes, "UTF-8");
        System.out.println(remoteAddress+":"+s1);
        channel.writeAndFlush("[server recv]"+s1);
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+"Online.");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+"Offline.");
    }
}

2. client

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
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.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.net.InetSocketAddress;
import java.util.Scanner;

public class Client {
    private String host;
    private int port;

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

    public void start(){
    	// Similar to server side
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(host, port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("handler", new ClientHandler());
                            pipeline.addLast("decoder", new StringDecoder());
                            pipeline.addLast("encoder", new StringEncoder());
                        }
                    });
            Channel channel = bootstrap.connect().sync().channel();
            System.out.println("Client successfully connected to server");
            Scanner scanner = new Scanner(System.in);
            scanner.useDelimiter("\n");

            while (scanner.hasNext()) {
                String msg = scanner.nextLine();
                if ("exit".equals(msg)) {
                    System.out.println("I'm offline.");
                    channel.close();
                    return;
                }
                if (" ".equals(msg)) {
                    continue;
                }
                channel.writeAndFlush(msg);
            }
        } catch (Exception e) {
            e.getStackTrace();
        } finally {
            group.shutdownGracefully();
            System.out.println("Client is down");
        }
    }
}

Clientandler class: mainly responsible for the function implementation of the client

public class ClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);

        String s1 = new String(bytes, "UTF-8");
        System.out.println(s1);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
//        System.out.println("recv:"+msg);
    }
}

Note: there is a problem here. Sometimes the channelRead() method can read the information in the channel. Sometimes the channelRead0() method can read the information in the same channel. Personal guess is related to the JDK version. When using, you should try which method is available first. The difference between the two methods is that the information obtained by the channelRead() method needs to be converted into String type through ByteBuf, while the channel type The read0() method gets the String type directly.

3. Start of server and client
The server:

public class ServerTest {
    public static void main(String[] args) {
        Server server = new Server();
        server.bind(9999);
    }
}

Client:

public class ClientTest {
    public static void main(String[] args) {
        Client client = new Client("127.0.0.1", 9999);
        client.start();
    }
}

Topics: Netty socket codec Java