Create and start a Netty server
How do I create and start a Netty server? Start a netty server and listen on port 8888 with the following code.
public class MyNettyServer { private static Logger logger = LogManager.getLogger(MyNettyServer.class); public static void main(String[] args) throws InterruptedException { NioEventLoopGroup boss = new NioEventLoopGroup(1); NioEventLoopGroup worker = new NioEventLoopGroup(); try { new ServerBootstrap() .group(boss, worker)//This method returns ServerBootstrap .option(ChannelOption.SO_BACKLOG, 128)//This method returns ServerBootstrap .channel(NioServerSocketChannel.class)//This method returns ServerBootstrap .handler(new MyServerChannelDuplexHandler())//This method returns ServerBootstrap .childHandler(new MyChannelInitializer("Client"))//This method returns ServerBootstrap .bind(8888)//Return to ChannelFuture .sync()//Return to ChannelFuture .addListener(future -> logger.info("Service started successfully:" + future.isSuccess()))//Return to ChannelFuture .channel()//Return to Channel .closeFuture()//Return to ChannelFuture .sync()//Return to ChannelFuture .addListener(future -> logger.info("Port closed,If there is an exception, the cause of the exception is:"+future.cause()));//Return to ChannelFuture } finally { //Elegant shutdown of boss and worker boss.shutdownGracefully(); worker.shutdownGracefully(); } } }
- NioEventLoopGroup boss: Equivalent to a circular listening port operation, when a client initiates a connection, it gets a socket operation corresponding to the client for communication between the two sides of the end
- NioEventLoopGroup worker: The above boss gives the socket to the worker after creating the corresponding socket with the client, and the worker performs the specific communication operation.
- NioEventLoopGroup: Asynchronous Event Loop Executor Group, which defines a set of asynchronous event executors, NioEventLoop. For the time being, it can be simply understood as the relationship between thread pools and threads, which will be specially analyzed later.
- ServerBootstrap: A service startup boot class that allows you to quickly build a Netty application and provides a chain programming
- group(): Configure boss and worker to simply have the current ServerBootstrap hold boss and work.
- option(ChannelOption.SO_BACKLOG, 128): Configure parameters for channel, this example configures the number of queues to be processed for link requests 128, and link requests over 128 will be rejected
- channel(): Configure the type of channel, using OIOServerSocketChannel and other channels in addition to NioServerSocketChannel
- hander(): Configure the box's channel processor, that is, how to handle connection requests, which are usually distributed to the worker, so the box has a fixed request forwarding handler that forwards all link requests to the worker.
- childHandler(): Configure the channel processor of the worker, and the read and write operations on the channel are ultimately implemented through the channel hander
- bind(): Groups and channel s must be configured in the above configuration information, and the bind method will start the netty service and listen for the corresponding addresses and ports
- sync(): Since the bind() method is internally asynchronous, the sync() method waits for the bind() asynchronous result
- addListener(): Adds a listener to an asynchronous result and is notified when the result is executed
- channel(): Gets the channel in the asynchronous execution result
- CloseFuture: Gets the asynchronous closing result of a channel, which cannot be closed for the ServerSocketChannel port, so closeFure will not complete, for SocketChannel, when channel is called on the opposite side. The close() method will be closed, so the closeFuture will be assigned at this time
- sync(): Wait for channel closure results on closeFuture
- addListener(): When the channel is closed, listeners registered on the channel will be notified
Create a client and connect to it
Creating a good client logic based on the Netty framework is basically the same as the client, except that the client does not need a boss listening port and uses Bootstrap to boot the creation
class MyNettyClient { private static Logger logger = LogManager.getLogger(MyNettyServer.class); public static void main(String[] args) throws InterruptedException { NioEventLoopGroup g = new NioEventLoopGroup(1); try { SocketChannel channel = (SocketChannel) new Bootstrap() .group(g) .channel(NioSocketChannel.class) .handler(new MyChannelInitializer("Server")) .connect(new InetSocketAddress(8888)) .sync() .addListener(future -> logger.info("Successful client startup:" + future.isSuccess())) .channel(); channel.write("First message"); channel.write(Stream.of("This is the second message", "Article 3", "Last message, very long message").collect(Collectors.toList())); channel.closeFuture() .sync() .addListener(future -> logger.info("Port closed,If there is an exception, the cause of the exception is:" + future.cause())); } finally { g.shutdownGracefully(); } } }
ChannelHandler for server-side boss
This ChannelHandler prints log input at different nodes of the server-side box
class MyServerChannelDuplexHandler extends ChannelDuplexHandler { private static Logger logger = LogManager.getLogger(MyServerChannelDuplexHandler.class); @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { logger.info("NioServerSocketChannel:" + ctx.channel() + "Successfully registered with the executor:" + ctx.executor()); ctx.fireChannelRegistered(); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { logger.info("NioServerSocketChannel:" + ctx.channel() + "Successfully added ChannelHandler:" + ctx.handler()); } @Override public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { logger.info("NioServerSocketChannel:" + ctx.channel() + "Perform binding to address:" + localAddress); ctx.bind(localAddress, promise); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { logger.info("NioServerSocketChannel:" + ctx.channel() + "Activated, ready to listen for port events"); ctx.fireChannelActive(); } }
Server-side worker and client-side ChannelHander
class MyChannelInitializer extends ChannelInitializer<SocketChannel> { private static Log log = LogFactory.getLog(MyChannelInitializer.class); private static final String delimiterStr = "$_-$"; private static final int maxFrameLen = 1024 * 1024;//1M private static final ByteBuf delimiter = ByteBufAllocator.DEFAULT.directBuffer(4).writeBytes(delimiterStr.getBytes()); private String peer; public MyChannelInitializer(String peer) { this.peer = peer; } private void check(ChannelHandlerContext ctx, String msg) throws Exception { if (msg.length() > maxFrameLen) { exceptionCaught(ctx, new TooLongFrameException("Sending message content is too long")); } } @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new DelimiterBasedFrameDecoder(maxFrameLen, delimiter)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new ChannelDuplexHandler() { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { StringBuilder res = new StringBuilder(); if (msg instanceof String) { String str = (String) msg; check(ctx, str); res.append(str).append(delimiterStr); } else if (msg instanceof List && ((List) msg).get(0) instanceof String) { for (String str : (List<String>) msg) { check(ctx, str); res.append(str).append(delimiterStr); } } else { exceptionCaught(ctx, new Error("Only send is currently allowed String and List<String>type")); } ctx.write(res); ctx.flush(); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { super.channelReadComplete(ctx); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { log.info("Receive Server [" + ctx.channel().remoteAddress() + "]Message [" + msg + "]"); if (peer == "Client") { ctx.pipeline().write("Message successfully processed"); ctx.flush(); } } }); } }
Start test
- Start the server first, and you will get the following log information, executed in the order of log order:
- Register channelHandler with channel
- Register channel with executor
- Port Binding
- Execute Port Binding Listener
- Channel Activation Starts Listening for Port Events
21:59:34.993 [nioEventLoopGroup-2-1] INFO netty.server.MyServerChannelDuplexHandler - NioServerSocketChannel:[id: 0xa12ccdd6]Successfully added ChannelHandler:netty.server.MyServerChannelDuplexHandler@1a89455d 21:59:34.993 [nioEventLoopGroup-2-1] INFO netty.server.MyServerChannelDuplexHandler - NioServerSocketChannel:[id: 0xa12ccdd6]Successfully registered with the executor: io.netty.channel.nio.NioEventLoop@43301423 21:59:34.993 [nioEventLoopGroup-2-1] INFO netty.server.MyServerChannelDuplexHandler - NioServerSocketChannel:[id: 0xa12ccdd6]Execute binding to address:0.0.0.0/0.0.0.0:8888 21:59:35.009 [nioEventLoopGroup-2-1] INFO netty.server.MyNettyServer - Service started successfully: true 21:59:35.009 [nioEventLoopGroup-2-1] INFO netty.server.MyServerChannelDuplexHandler - NioServerSocketChannel:[id: 0xa12ccdd6, L:/0:0:0:0:0:0:0:0:8888]Activated, ready to listen for port events
Start Client Link to Client
When the client starts, the following log will be output, executed in the following order
-Notify listeners on connect ion asynchronous results
- Send messages to the server
- Accept server response results and output
22:01:09.043 [nioEventLoopGroup-2-1] INFO netty.server.MyNettyServer - Successful client startup: true 22:01:09.090 [nioEventLoopGroup-2-1] INFO netty.server.MyChannelInitializer - Receive server [0].0.0.0/0.0.0.0:8888]Message [Successfully processed message] 22:01:09.090 [nioEventLoopGroup-2-1] INFO netty.server.MyChannelInitializer - Receive server [0].0.0.0/0.0.0.0:8888]Message [Successfully processed message] 22:01:09.090 [nioEventLoopGroup-2-1] INFO netty.server.MyChannelInitializer - Receive server [0].0.0.0/0.0.0.0:8888]Message [Successfully processed message] 22:01:09.090 [nioEventLoopGroup-2-1] INFO netty.server.MyChannelInitializer - Receive server [0].0.0.0/0.0.0.0:8888]Message [Successfully processed message]
Service side processes client messages
22:01:09.075 [nioEventLoopGroup-3-1] INFO netty.server.MyChannelInitializer - Receive Server [/192.168.56.1:61353]Message [First Message] 22:01:09.075 [nioEventLoopGroup-3-1] INFO netty.server.MyChannelInitializer - Receive Server [/192.168.56.1:61353]Message [This is the second message] 22:01:09.075 [nioEventLoopGroup-3-1] INFO netty.server.MyChannelInitializer - Receive Server [/192.168.56.1:61353]Message [Article 3] 22:01:09.075 [nioEventLoopGroup-3-1] INFO netty.server.MyChannelInitializer - Receive Server [/192.168.56.1:61353]Message [Last message, very long message]