Netty source code practice–echo

Netty source code practice

To learn netty, you can start from the netty-example module of netty source code.

netty-example has an example echo, which is very suitable for introductory learning.

Here it is slightly modified and used as an example for learning.

Introduce dependency packages:

 <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.29.Final</version>
        </dependency>

Server

After receiving the client’s message, the server will respond.

  • EchoServer:
/**
 * After receiving the client's message, the server will respond.
 */
public final class EchoServer {
    /**
     * port
     */
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        //Configure EventLoopGroup
        // Master-slave Reactor multi-thread mode, bossGroup is the master Reactor, workerGroup is the slave Reactor
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //Initialize the server's boot class ServerBootstrap
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //Specify EventLoopGroup
            serverBootstrap.group(bossGroup, workerGroup)
                //Specify channel
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
                //Specify ChannelHandler for processing channel
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     //ChannelPipeline, based on the responsibility chain model, can add multiple ChannelHandlers
                     ChannelPipeline channelPipeline = ch.pipeline();
                     //channelPipeline.addLast(new LoggingHandler(LogLevel.INFO));
                     //ChannelHandler, used to process channels, process received data, and implement business logic.
                     channelPipeline.addLast(new EchoServerHandler());
                 }
             });

            // Start the server and bind the server to the port on which it will listen for connection requests.
            ChannelFuture channelFuture = serverBootstrap.bind(PORT).sync();

            // Wait until the server socket is closed
            channelFuture.channel().closeFuture().sync();
        } finally {
            //Close all eventLoops and terminate threads
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

  • EchoServerHandler:

ChannelHandler is used to process channels and implement business logic.

/**
 * ChannelHandler on the server side.
 *
 * ChannelHandler, used to process channels, process received data, and implement business logic.
 * Inherit ChannelInboundHandlerAdapter to define methods for responding to inbound events.
 *
 */
@Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * channelRead(): Read the message passed in by channel
     *
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf buf= (ByteBuf) msg;
        log.info("Message from client: " + buf.toString(CharsetUtil.UTF_8) + "\
");
    }

    /**
     * channelReadComplete(): Indicates that the current ChannelHandler has finished reading.
     * After execution, it will automatically jump to the next ChannelHandler in ChannelPipeline.
     *
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        //Return data to the client, writeAndFlush() can also be split into write(msg) and flush()
        ctx.writeAndFlush(Unpooled.copiedBuffer("I'm happy to see you too^_^",CharsetUtil.UTF_8));
    }

    /**
     * exceptionCaught(): Called when an exception is thrown during the read operation.
     *
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        //Close the connection when an exception occurs
        cause.printStackTrace();
        ctx.close();
    }
}

Client

In the following example, the client sends a message to the server.

  • EchoClient:
/**
 * client. Send data to the server and receive the response from the server.
 *
 */
public final class EchoClient {

    static final String HOST = System.getProperty("host", "127.0.0.1");
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));

    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
             .channel(NioSocketChannel.class)
             .option(ChannelOption.TCP_NODELAY, true)
             .handler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel socketChannel) throws Exception {
                     ChannelPipeline channelPipeline = socketChannel.pipeline();
                     //channelPipeline.addLast(new LoggingHandler(LogLevel.INFO));
                     //ChannelHandler, used to process channels, process received data, and implement business logic.
                     channelPipeline.addLast(new EchoClientHandler());
                 }
             });

            //Open the client and connect to the port of the server
            ChannelFuture channelFuture = bootstrap.connect(HOST, PORT).sync();

            channelFuture.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

  • EchoClientHandler:
/**
 * Client's ChannelHandler.
 */
public class EchoClientHandler extends ChannelInboundHandlerAdapter {


    /**
     * channelActive() will be called after the connection between the client and the server is established.
     *
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ByteBuf firstMessage = Unpooled.buffer(EchoClient.SIZE);
        byte[] bytes = "Nice to meet you^_^\
".getBytes(CharsetUtil.UTF_8);
        firstMessage.writeBytes(bytes);
        //Send data to server
        ctx.writeAndFlush(firstMessage);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf buf = (ByteBuf) msg;
        log.info("Message from the server: " + buf.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

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

Execution:

Start the server first, then the client.

The server receives information from the client:

11:25:03.081 [nioEventLoopGroup-3-1] INFO com.example.demo.netty.echo.EchoServerHandler - Message from client: Nice to meet you^_^

The client receives the reply from the server:

11:25:03.091 [nioEventLoopGroup-2-1] INFO com.example.demo.netty.echo.EchoClientHandler - Message from the server: I am also very happy to meet you^_^

Netty common classes

The above example uses Netty’s commonly used classes.

For details, see: https://blog.csdn.net/sinat_32502451/article/details/133934402

Reference materials:

https://zhuanlan.zhihu.com/p/415450910