============================== System.exit() method ================= ==============
Prototype: System.exit(int status)
Its function is mainly to call Runtime.getRuntime().exit(status);
The function is to terminate the currently running Java virtual machine. This status indicates the exit status code, and non-zero indicates abnormal termination. (You can return a call return code to the caller of other processes to adopt different strategies according to the return code.)
Note: No matter what the status is, the program will exit. Compared with return, the difference is: return returns to the previous layer, while System.exit(status) returns to the top layer.
System. exit(0):
- Exit normally, the program exits after normal execution, Java GC performs garbage collection, and exits directly.
- In Swing development, it is generally used for the Swing form close button. (When rewriting the windowClosing method, call System.exit(0) to terminate the program. The dispose() method of the Window class just closes the window and does not allow the program to exit).
System. exit(1):
- Is an abnormal exit, that is to say, the program exits regardless of whether it is executing or not.
- If non-zero, this method will block indefinitely if the virtual machine has started a shutdown sequence after this method is called. If a shutdown hook is running, this method will block indefinitely. If off hooks run to completion, and finalizers are not called, start recycling completes if finalization-on-exit allows, and the virtual machine stops.
- Generally, it will be used in the catch block (for example, when using Apache’s FTPClient class, it is recommended to use System.exit(1) in the source code to inform the connection failure), when the program will be called by the script, and the parent process calls abnormally, it needs to pass System.exit( 1) to inform that the operation failed. The default value returned by the program is 0. Even if an exception occurs, the default value is still 0. Therefore, in this case, it is necessary to manually specify the return value to be non-zero.
================================= Nettty Code ================= ===================
Netty client:
package org.jy.sso.websocket.stomp.push.netty.chat.system.chatcenter; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import lombok. SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.time.DateFormatUtils; import org.jy.sso.websocket.stomp.push.netty.chat.system.chapter.handler.FirstClientSendMsgHandler; import org.jy.sso.websocket.stomp.push.netty.chat.system.chatcommond.ChatRetryCommand; import org.jy.sso.websocket.stomp.push.netty.chat.system.constant.ChatClientConstant; import java.util.Date; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; /** * IM chat system client * <p> * The tool mainly encapsulates the java.util.Random object and provides some methods for generating random numbers or random strings. The random tool class is RandomUtil, and the methods in it are all static methods. */ @Slf4j public class ChatCenterClient { public static final ConcurrentHashMap<Byte,Bootstrap> bootStrapMap = new ConcurrentHashMap(); @SneakyThrows public static void main(String[] args) { Bootstrap bootstrap = new Bootstrap(); NioEventLoopGroup group = new NioEventLoopGroup(); bootstrap. group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel channel) { // Add a logical processor on the client side, and write data to the server after the client successfully establishes a connection channel.pipeline().addLast(new FirstClientSendMsgHandler()); } }); connect(bootstrap, ChatClientConstant.CHAT_SERVER_HOST_IP, ChatClientConstant.CHAT_SERVER_PORT, ChatClientConstant.CHAT_MAX_RETRY_NUM); // The mapping relationship between the reconnection command and the instance started by the corresponding client bootStrapMap.put(ChatRetryCommand.CHAT_RETRY_CONNECTION, bootstrap); } /** * @param bootstrap client enabler * @param host host * @param port port * @param retryNum number of exponential backoff reconnections * @author yh19166 * @deprecated connection and reconnection mechanism, realizing exponential backoff reconnection */ private static void connect(Bootstrap bootstrap, String host, int port, int retryNum) { bootstrap.connect(host, port).addListener(future -> { // Use future.isSuccess() to determine whether the connection is successful if (future. isSuccess()) { System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " -->> Connection succeeded...."); } else if (retryNum == ChatClientConstant. CHAT_INI_RETRY_NUM) { System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " -->> The number of reconnections has been exhausted, giving up the connection.... "); /** * Prototype: System.exit(int status) * * Its function is mainly to call Runtime.getRuntime().exit(status); * * The function is to terminate the currently running Java virtual machine. This status indicates the exit status code, and non-zero indicates abnormal termination. (You can return a call return code to the caller of other processes to adopt different strategies according to the return code.) * * Note: No matter what the status is, the program will exit. Compared with return, the difference is: return returns to the previous layer, while System.exit(status) returns to the top layer. *==================================================== ==================================================== ==================================================== == * System. exit(0): * * Normal exit, the program exits after normal execution, Java GC performs garbage collection, and exits directly. * In Swing development, it is generally used for the Swing form close button. (When rewriting the windowClosing method, call System.exit(0) to terminate the program. The dispose() method of the Window class just closes the window and does not allow the program to exit). * System. exit(1): * * Abnormal exit, that is to say, exit regardless of whether the program is executing or not. * If non-zero, if this method is called, the virtual machine has started the shutdown sequence If the shutdown hook is running, this method will block indefinitely. If off hooks run to completion, and finalizers are not called, start recycling completes if finalization-on-exit allows, and the virtual machine stops. * It is generally used in the catch block (for example, when using Apache's FTPClient class, it is recommended to use System.exit(1) in the source code to inform the connection failure), when the program will be called by the script and the parent process calls abnormally, it needs to pass System.exit (1) to inform that the operation failed. The default value returned by the program is 0. Even if an exception occurs, the default value is still 0. Therefore, in this case, it is necessary to manually specify the return value to be non-zero. */ System. exit(0); } else { // How many times to reconnect int order = (ChatClientConstant.CHAT_MAX_RETRY_NUM - retryNum) + ChatClientConstant.CHAT_STEP_RETRY_LENGTH; // The client considers the logic of reconnection, sub-gradient connection... System.out.println("No. " + order + " connection failed..."); // Interval of this reconnection: Quickly calculate the reconnection time interval by shifting left int delay = ChatClientConstant. CHAT_STEP_RETRY_LENGTH << order; System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " -->> connection failed, the first " + order + "restart connect...."); // Implement timing task logic bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retryNum - ChatClientConstant.CHAT_STEP_RETRY_LENGTH), delay, TimeUnit.SECONDS); } }); } /** * @param bootstrap client startup class * @param host host * @param port port * @param retryNum number of exponential backoff reconnects * @author yh19166 * @deprecated connection and reconnection mechanism, realizing exponential backoff reconnection */ @Deprecated public static void retryConnect(Bootstrap bootstrap, String host, int port, int retryNum) { bootstrap.connect(host, port).addListener(future -> { if (future. isSuccess()) { log.info("Connect to the server successfully!"); } else if (retryNum == ChatClientConstant. CHAT_INI_RETRY_NUM) { log.error("The number of reconnections has been exhausted, give up the connection!"); /** * Prototype: System.exit(int status) * * Its function is mainly to call Runtime.getRuntime().exit(status); * * The function is to terminate the currently running Java virtual machine. This status indicates the exit status code, and non-zero indicates abnormal termination. (You can return a call return code to the caller of other processes to adopt different strategies according to the return code.) * * Note: No matter what the status is, the program will exit. Compared with return, the difference is: return returns to the previous layer, while System.exit(status) returns to the top layer. *==================================================== ==================================================== ==================================================== == * System. exit(0): * * Normal exit, the program exits after normal execution, Java GC performs garbage collection, and exits directly. * In Swing development, it is generally used for the Swing form close button. (When rewriting the windowClosing method, call System.exit(0) to terminate the program. The dispose() method of the Window class just closes the window and does not allow the program to exit). * System. exit(1): * * Abnormal exit, that is to say, exit regardless of whether the program is executing or not. * If non-zero, if this method is called, the virtual machine has started the shutdown sequence If the shutdown hook is running, this method will block indefinitely. If off hooks run to completion, and finalizers are not called, start recycling completes if finalization-on-exit allows, and the virtual machine stops. * It is generally used in the catch block (for example, when using Apache's FTPClient class, it is recommended to use System.exit(1) in the source code to inform the connection failure), when the program will be called by the script and the parent process calls abnormally, it needs to pass System.exit (1) to inform that the operation failed. The default value returned by the program is 0. Even if an exception occurs, the default value is still 0. Therefore, in this case, it is necessary to manually specify the return value to be non-zero. */ System. exit(0); } else { // How many times to reconnect int order = (ChatClientConstant.CHAT_MAX_RETRY_NUM - retryNum) + ChatClientConstant.CHAT_STEP_RETRY_LENGTH; // The interval for this reconnection int delay = ChatClientConstant. CHAT_STEP_RETRY_LENGTH << order; log.error(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + ": Connection failed, reconnecting at " + order + ".... "); bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retryNum - ChatClientConstant.CHAT_STEP_RETRY_LENGTH), delay, TimeUnit.SECONDS); } }); } }
Constant classes used in the code:
package org.jy.sso.websocket.stomp.push.netty.chat.system.constant; public class ChatClientConstant { // Host IP public static final String CHAT_SERVER_HOST_IP = "127.0.0.1"; // port to connect to public static final int CHAT_SERVER_PORT = 8000; // maximum number of retries public static final int CHAT_MAX_RETRY_NUM = 4; // retry initial value public static final int CHAT_INI_RETRY_NUM = 0; // step size public static final int CHAT_STEP_RETRY_LENGTH = 1; }
Client Handler:
package org.jy.sso.websocket.stomp.push.netty.chat.system.chapter.handler; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.time.DateFormatUtils; import org.jy.sso.websocket.stomp.push.netty.chat.system.chatcenter.ChatCenterClient; import org.jy.sso.websocket.stomp.push.netty.chat.system.chatcommond.ChatRetryCommand; import org.jy.sso.websocket.stomp.push.netty.chat.system.constant.ChatClientConstant; import java.nio.charset.StandardCharsets; import java.util.Date; /** * The client sends a message to the server. The first handler */ @Slf4j public class FirstClientSendMsgHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) { System.out.println("=================================================================================================================== =================="); System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + ": Client writes out data"); // 1. Get data ByteBuf buffer = getByteBuf(ctx); // 2. Write data ctx.channel().writeAndFlush(buffer); } private ByteBuf getByteBuf(ChannelHandlerContext ctx) { // 1. Get the binary abstract ByteBuf ByteBuf buffer = ctx.alloc().buffer(); // 2. Prepare the data to be passed to the server, and specify the character encoding as UTF-8 byte[] bytes = "Hello, this is the data passed by the Netty client".getBytes(StandardCharsets.UTF_8); // 3. Fill data into ByteBuf buffer.writeBytes(bytes); return buffer; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { System.out.println("================================================================================================================== ==================="); ByteBuf byteBuf = (ByteBuf) msg; System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " : the data passed by the client read by the server -> " + byteBuf.toString(StandardCharsets.UTF_8)); // Remove this encoding and convert the result to: } @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } /** * Program robustness considerations * * @param ctx channel processor context * @param cause The error message thrown */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // Exception caused by client not exiting normally: An existing connection was forcibly closed by the remote host // The server is closed suddenly, throwing this exception: The remote host forcibly closed an existing connection ctx.channel().flush(); ctx. channel(). closeFuture(); log.info("FirstClientSendMsgHandler.exceptionCaught : [{}]", cause.getMessage()); Bootstrap Bootstrap = ChatCenterClient.bootStrapMap.get(ChatRetryCommand.CHAT_RETRY_CONNECTION); ChatCenterClient.retryConnect(Bootstrap, ChatClientConstant.CHAT_SERVER_HOST_IP, ChatClientConstant.CHAT_SERVER_PORT, ChatClientConstant.CHAT_MAX_RETRY_NUM); } }
===================================== Netty server side ============== ==================
package org.jy.sso.websocket.stomp.push.netty.chat.system.chatcenter; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelInitializer; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import lombok. SneakyThrows; import org.jy.sso.websocket.stomp.push.netty.chat.system.chapter.handler.FirstServerReceiveDataHandler; /** * IM server side */ public class ChatCenterServer { @SneakyThrows public static void main(String[] args) { // server-side startup class ServerBootstrap serverBootstrap = new ServerBootstrap(); // boss main thread (parent thread) NioEventLoopGroup bossLoopGroup = new NioEventLoopGroup(); // worker thread (child thread) NioEventLoopGroup workerLoopGroup = new NioEventLoopGroup(); serverBootstrap // Parent and child threads establish a group connection .group(bossLoopGroup, workerLoopGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) { ch.pipeline().addLast(new FirstServerReceiveDataHandler()); } }).bind(8000).sync(); } }
The channel processor handler corresponding to the server-side Pipeline:
package org.jy.sso.websocket.stomp.push.netty.chat.system.chapter.handler; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.time.DateFormatUtils; import java.nio.charset.StandardCharsets; import java.util.Date; /** * Processor for receiving client data on the server side: * The server side receives the client data * Respond to client requests and write data to the client */ @Slf4j public class FirstServerReceiveDataHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object requestContent) { //Receive the data logic of the client System.out.println("============================== Receive client data =========== ====================="); ByteBuf byteBuf = (ByteBuf) requestContent; System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " : the data passed by the client read by the server -> " + byteBuf.toString(StandardCharsets.UTF_8)); // Remove this encoding and convert the result to: System.out.println("==============================Write data to the client ========= ======================="); ByteBuf response = getByteBuf(ctx); ctx.channel().writeAndFlush(response); } /** * Write response data to the client * * @param ctx channel processor context * @return {@link io.netty.buffer.ByteBuf} */ private ByteBuf getByteBuf(ChannelHandlerContext ctx) { byte[] responseBytes = "Hello, welcome to pay attention to Brother Yang's WeChat official account, <<financial freedom cultivator>>!".getBytes(StandardCharsets.UTF_8); ByteBuf buffer = ctx.alloc().buffer(); buffer.writeBytes(responseBytes); return buffer; } @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // The client did not close normally, an exception was thrown: The host forcibly closed an existing connection ctx.channel().flush(); ctx. channel(). closeFuture(); log.info("exception: [{}]",cause.getMessage()); // You can consider restarting the server, the difference between restarting|reconnecting } }
The test results are getting worse:
======================== Run the client code first ============= Do not run the server code === =================== 20:04:33.435 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.tinyCacheSize: 512 20:04:33.435 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.smallCacheSize: 256 20:04:33.435 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.normalCacheSize: 64 20:04:33.435 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedBufferCapacity: 32768 20:04:33.435 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimInterval: 8192 20:04:33.448 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled 20:04:33.448 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 65536 20:04:33.448 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384 1st connection failed... 2023-05-24 20:04:34 -->> Connection failed, reconnecting for the first time.... Connection 2 failed... 2023-05-24 20:04:37 -->> Connection failed, reconnecting for the second time.... 3rd connection failed... 2023-05-24 20:04:42 -->> Connection failed, reconnected for the third time.... 4th connection failed... 2023-05-24 20:04:51 -->> Connection failed, reconnecting for the 4th time.... 2023-05-24 20:05:08 -->> The number of reconnections has been exhausted, giving up the connection.... Process finished with exit code 0 =============================== Client output log ================= =========================
====== Run the server-side code first, then run the client-side code, after the client connects to the server, then close the server-side =============== 20:27:31.730 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.normalCacheSize: 64 20:27:31.730 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedBufferCapacity: 32768 20:27:31.730 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimInterval: 8192 20:27:31.748 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled 20:27:31.748 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 65536 20:27:31.748 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384 2023-05-24 20:27:31 -->> Connection succeeded.... ============================ Write data to the server ====================== ======== 2023-05-24 20:27:31: Client writes data 20:27:31.801 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 32768 20:27:31.801 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2 20:27:31.801 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16 20:27:31.809 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8 20:27:31.841 [nioEventLoopGroup-2-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.bytebuf.checkAccessible: true 20:27:31.841 [nioEventLoopGroup-2-1] DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@5f1d79 ====================================================================================================================== ========= 2023-05-24 20:27:31 : The data read by the server and transmitted by the client -> Hello, welcome to pay attention to Brother Yang’s WeChat official account, <<Financial Freedom Cultivator>>! 20:27:37.561 [nioEventLoopGroup-2-1] INFO org.jy.sso.websocket.stomp.push.netty.chat.system.chapter.handler.FirstClientSendMsgHandler - FirstClientSendMsgHandler.exceptionCaught: [The remote host forcibly closed an existing Connection. ] 20:27:38.569 [nioEventLoopGroup-2-2] ERROR org.jy.sso.websocket.stomp.push.netty.chat.system.chatcenter.ChatCenterClient - 2023-05-24 20:27:38: Connection failed, page 1 reconnect.... Connection 2 failed... 2023-05-24 20:27:41 -->> Connection failed, reconnecting for the second time.... 3rd connection failed... 2023-05-24 20:27:46 -->> Connection failed, reconnected for the third time.... 4th connection failed... 2023-05-24 20:27:55 -->> Connection failed, reconnecting for the 4th time.... 2023-05-24 20:28:12 -->> The number of reconnections has been exhausted, giving up the connection.... Process finished with exit code 0 ================================== Client output log =============== ==================