Netty core principles: 2. Intermediate expansion-08: Netty heartbeat service and disconnection reconnection

Article directory

  • 1. Introduction
  • 2. Code implementation
    • 2.1 Engineering structure
    • 2.2 Netty client
      • 2.2.1 Client Processor
      • 2.2.2 Client channel initialization
      • 2.2.3 Client channel listener
      • 2.2.4 Client
    • 2.3 Netty server
      • 2.3.1 Server-side processor
      • 2.3.2 Server channel initialization
      • 2.3.3 Server
  • 3. Unit testing

1. Introduction

When using Netty, it is necessary to monitor whether the service is stable and to automatically reconnect when the network link is disconnected due to abnormal network conditions.
Need to listen: f.addListener(new MyChannelFutureListener())

2. Code implementation

2.1 Project Structure

netty-2.0-08
|-src
    |-main
         |-java
             |-com.lino.netty
|-client
| |-MyChannelFutureListener.java
| |-MyChannelInitializer.java
| |-MyClientHandler.java
| |-NettyClient.java
|-server
| |-MyChannelInitializer.java
| |-MyServerHandler.java
| |-NettyServer.java

2.2 Netty client

2.2.1 Client Processor

MyClientHandler.java

package com.lino.netty.client;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.SocketChannel;
import java.time.LocalDateTime;

/**
 * @description: client processor
 */
public class MyClientHandler extends ChannelInboundHandlerAdapter {<!-- -->

    /**
     * When the client actively connects to the server's connection, this channel is active. That is, the client and server have established a communication channel and can transmit data.
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {<!-- -->
        SocketChannel channel = (SocketChannel) ctx.channel();
        System.out.println("Link report starts");
        System.out.println("Link report information: This client is connected to the server. channelId: " + channel.id());
        System.out.println("Link reporting IP:" + channel.localAddress().getHostString());
        System.out.println("Link report Port:" + channel.localAddress().getPort());
        System.out.println("Link report completed");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {<!-- -->
        System.out.println("Disconnect and reconnect" + ctx.channel().localAddress().toString());
        // Use process interrupt line to reconnect
        new Thread(new Runnable() {<!-- -->

            @Override
            public void run() {<!-- -->
                try {<!-- -->
                    new NettyClient().connect("127.0.0.1", 7397);
                    System.out.println("lino-learn-netty client start done.");
                    Thread.sleep(500);
                } catch (Exception e) {<!-- -->
                    System.out.println("lino-learn-netty client start error go reconnect.");
                }
            }
        }).start();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {<!-- -->
        //Receive msg message
        System.out.println(LocalDateTime.now() + "Message received: " + msg);
    }

    /**
     * Catch exceptions. When an exception occurs, you can do some corresponding processing, such as printing logs and closing links.
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {<!-- -->
        System.out.println("Exception message, disconnect and reconnect:\r\
" + cause.getMessage());
        ctx.close();
    }
}

2.2.2 Client channel initialization

MyChannelInitializer.java

package com.lino.netty.client;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.nio.charset.Charset;

/**
 * @description: Client channel initialization
 */
public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {<!-- -->
    @Override
    protected void initChannel(SocketChannel channel) throws Exception {<!-- -->
        // Convert encoding to String, pay attention to adjusting your own encoding format GBK, UTF-8
        channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
        // Decode to String, pay attention to adjust your own encoding format GBK, UTF-8
        channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
        //Add yourself to the receiving data implementation method in the pipeline
        channel.pipeline().addLast(new MyClientHandler());
    }
}

2.2.3 Client Channel Listener

MyChannelFutureListener.java

package com.lino.netty.client;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.EventLoop;
import java.util.concurrent.TimeUnit;

/**
 * @description: Channel listener
 */
public class MyChannelFutureListener implements ChannelFutureListener {<!-- -->

    @Override
    public void operationComplete(ChannelFuture channelFuture) throws Exception {<!-- -->
        if (channelFuture.isSuccess()) {<!-- -->
            System.out.println("lino-learn-netty client start done.");
            return;
        }
        final EventLoop loop = channelFuture.channel().eventLoop();
        loop.schedule(new Runnable() {<!-- -->

            @Override
            public void run() {<!-- -->
                try {<!-- -->
                    new NettyClient().connect("127.0.0.1", 7397);
                    System.out.println("lino-learn-netty client start done.");
                    Thread.sleep(500);
                } catch (Exception e) {<!-- -->
                    System.out.println("lino-learn-netty client start error go reconnect.");
                }
            }
        }, 1L, TimeUnit.SECONDS);
    }
}

2.2.4 Client

NettyClient.java

package com.lino.netty.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * @description: client
 */
public class NettyClient {<!-- -->

    public static void main(String[] args) {<!-- -->
        new NettyClient().connect("127.0.0.1", 7397);
    }

    public void connect(String inetHost, int inetPort) {<!-- -->
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {<!-- -->
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.AUTO_READ, true);
            b.handler(new MyChannelInitializer());
            ChannelFuture f = b.connect(inetHost, inetPort).sync();
            //Add monitoring and handle reconnection
            f.addListener(new MyChannelFutureListener());
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {<!-- -->
            e.printStackTrace();
        } finally {<!-- -->
            workerGroup.shutdownGracefully();
        }
    }
}

2.3 Netty server

2.3.1 Server-side processor

MyServerHandler.java

package com.lino.netty.server;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * @description: Server-side processor
 */
public class MyServerHandler extends ChannelInboundHandlerAdapter {<!-- -->

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {<!-- -->
        super.userEventTriggered(ctx, evt);
        if (evt instanceof IdleStateEvent) {<!-- -->
            IdleStateEvent e = (IdleStateEvent) evt;
            if (e.state() == IdleState.READER_IDLE) {<!-- -->
                System.out.println("Reminder => Reader Idle");
                ctx.writeAndFlush("Reading waiting: Are you there, client? [ctx.close()]{I end with a newline character for processing half-packed sticky packets}... ...\
");
                ctx.close();
            } else if (e.state() == IdleState.WRITER_IDLE) {<!-- -->
                System.out.println("Reminder => Writer Idle");
                ctx.writeAndFlush("Write waiting: Client are you there {I end with a newline character for processing half-packet sticky packets}... ...\
");
            } else if (e.state() == IdleState.ALL_IDLE) {<!-- -->
                System.out.println("Reminder => All Idle");
                ctx.writeAndFlush("All time: Client are you there {I end with a newline character for processing half-packet sticky packets}... ...\
");
            }
        }
        ctx.flush();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {<!-- -->
        SocketChannel channel = (SocketChannel) ctx.channel();
        System.out.println("Link report starts");
        System.out.println("Link report information: There is a client connected to this server. channelId: " + channel.id());
        System.out.println("Link reporting IP:" + channel.localAddress().getHostString());
        System.out.println("Link report Port:" + channel.localAddress().getPort());
        System.out.println("Link report completed");
        //Notify the client that the connection is successfully established
        String str = "Notify the client that the link is established successfully" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\
";
        ctx.writeAndFlush(str);
    }

    /**
     * When the client actively disconnects from the server, this channel is inactive.
     * That is to say, the communication channel between the client and the server is closed and data cannot be transmitted.
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {<!-- -->
        System.out.println("Client disconnected" + ctx.channel().localAddress().toString());
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {<!-- -->
        //Receive msg message
        System.out.println(LocalDateTime.now() + "Message received: " + msg);
        //Notify the client that the link message is sent successfully
        String str = "The server received: " + new Date() + " " + msg + "\r\
";
        ctx.writeAndFlush(str);
    }

    /**
     * Catch exceptions. When an exception occurs, you can do some corresponding processing, such as printing logs and closing links.
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {<!-- -->
        System.out.println("Exception message:\r\
" + cause.getMessage());
        ctx.close();
    }
}

2.3.2 Server channel initialization

MyChannelInitializer.java

package com.lino.netty.server;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import java.nio.charset.Charset;

/**
 * @description: Server channel initialization
 */
public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {<!-- -->

    @Override
    protected void initChannel(SocketChannel channel) {<!-- -->
        /**
         * Heartbeat detection
         * 1. readerIdleTimeSeconds read timeout
         * 2. writerIdleTimeSeconds write timeout time
         * 3. allIdleTimeSeconds read and write timeout
         * 4. TimeUnit.SECONDS seconds [default is seconds, can be specified]
         */
        channel.pipeline().addLast(new IdleStateHandler(2, 2, 2));
        // Convert encoding to String, pay attention to adjusting your own encoding format GBK, UTF-8
        channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
        // Decode to String, pay attention to adjust your own encoding format GBK, UTF-8
        channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
        //Add yourself to the receiving data implementation method in the pipeline
        channel.pipeline().addLast(new MyServerHandler());
    }
}

2.3.3 Server

NettyServer.java

package com.lino.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @description: netty server
 */
public class NettyServer {<!-- -->

    public static void main(String[] args) {<!-- -->
        new NettyServer().bind(7397);
    }

    public void bind(int port) {<!-- -->
        EventLoopGroup parentGroup = new NioEventLoopGroup();
        EventLoopGroup childGroup = new NioEventLoopGroup();
        try {<!-- -->
            ServerBootstrap b = new ServerBootstrap();
            b.group(parentGroup, childGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childHandler(new MyChannelInitializer());
            ChannelFuture f = b.bind(port).sync();
            System.out.println("lino-learn-netty server start done.");
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {<!-- -->
            e.printStackTrace();
        } finally {<!-- -->
            childGroup.shutdownGracefully();
            parentGroup.shutdownGracefully();
        }
    }
}

3. Unit testing

Start the NettyServer server

lino-learn-netty server start done.
Link report starts
Link report information: There is a client connected to this server. channelId: 04a06009
Link reporting IP: 127.0.0.1
Link Report Port: 7397
Link report completed
Reminder => Reader Idle
Client disconnected/127.0.0.1:7397
Link report starts
Link report information: There is a client connected to this server. channelId: 0f99d6d1
Link reporting IP: 127.0.0.1
Link Report Port: 7397
Link report completed
Reminder => Reader Idle
Client disconnected/127.0.0.1:7397
Link report starts
Link report information: There is a client connected to this server. channelId:ebbe72d6
Link reporting IP: 127.0.0.1
Link Report Port: 7397
Link report completed
Reminder => Reader Idle
Client disconnected/127.0.0.1:7397
Link report starts
Link report information: There is a client connected to this server. channelId: 179882e0
Link reporting IP: 127.0.0.1
Link Report Port: 7397
Link report completed
Exception information:
The remote host forcibly closed an existing connection.
Client disconnected/127.0.0.1:7397

Start the Netty client

Link report starts
Link report information: This client is linked to the server. channelId: 72cce3a8
Link reporting IP: 127.0.0.1
Link report Port: 63858
Link report completed
lino-learn-netty client start done.
2023-02-28T16:26:19.131 Message received: Notify the client that the link was successfully established Tue Feb 28 16:26:19 CST 2023 127.0.0.1

2023-02-28T16:26:21.092 Message received: Read waiting: Client are you there [ctx.close()]{<!-- -->I end with a newline character for processing half-package sticky packages }... ...

Disconnect and reconnect/127.0.0.1:63858
Link report starts
Link report information: This client is linked to the server. channelId: 1864a134
Link reporting IP: 127.0.0.1
Link Report Port: 63893
Link report completed
lino-learn-netty client start done.
2023-02-28T16:26:21.103 Message received: Notify the client that the link was successfully established Tue Feb 28 16:26:21 CST 2023 127.0.0.1

2023-02-28T16:26:23.119 Message received: Read waiting: Client, are you there [ctx.close()]{<!-- -->I end with a newline character for processing half-package sticky packages }... ...

Disconnect and reconnect/127.0.0.1:63893
lino-learn-netty client start done.
Link report starts
Link report information: This client is linked to the server. channelId:c9ad1077
Link reporting IP: 127.0.0.1
Link Report Port: 63926
Link report completed
lino-learn-netty client start done.
2023-02-28T16:26:23.135 Message received: Notify the client that the link is established successfully Tue Feb 28 16:26:23 CST 2023 127.0.0.1

2023-02-28T16:26:25.137 Message received: Read waiting: Client are you there [ctx.close()]{<!-- -->I end with a newline character for processing half-package sticky packages }... ...

Disconnect and reconnect/127.0.0.1:63926
lino-learn-netty client start done.
Link report starts
Link report information: This client is linked to the server. channelId: f56eed9f
Link reporting IP: 127.0.0.1
Link Report Port: 63959
Link report completed
lino-learn-netty client start done.
2023-02-28T16:26:25.163 Message received: Notify the client that the link is established successfully Tue Feb 28 16:26:25 CST 2023 127.0.0.1