Hello World for Netty server code
public class EchoServer { private final int port; public EchoServer(int port) { this.port = port; } public static void main(String[]args)throws Exception{ new EchoServer(8888).start(); } public void start() throws Exception{ final EchoServerHandler handler = new EchoServerHandler(); EventLoopGroup group = new NioEventLoopGroup(); try{ ServerBootstrap b = new ServerBootstrap(); b.group(group).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(handler); }}); ChannelFuture f = b.bind().sync(); f.channel().closeFuture().sync(); }finally { group.shutdownGracefully().sync(); }}}Copy the code
- Initialize EventLoopGroup
- Configure the bootstrap class ServerBootstrap as a tool to bootstrap channel creation
- Initialize the group used to process connection requests (namely acceptors) and the childGroup used to process events (namely clients)
The Hello Word version of the code uses the same NioEventLoop, which is usually assigned separately in practice
- Configure the type of channel to establish
- Set the port to which the server listens
- Configure the service’s own message handler, held through childHandler
- Create and initialize a channel
Adding ChannelInitializer at the end of the pipe adds a ServerBootstrapAcceptor (which is InboundHandler) to the pipe after the pipe is registered. It holds references to childGroup (Client) and childHandler, and the ChannelInitializer InboundHandler is removed from the channel once it has completed its mission.
ServerBootstrapAcceptor initially executes the call (subsequent read message call) when the client establishes a connection. The entry is doReadMessages. After reading the message, A read event is triggered along the InBoundHandler from Head to ServerBootstrapAcceptor, at which point a childGroup is registered to this channel, that is, the childGroup is used to process the read message each time
Public final void channelRegistered(ChannelHandlerContext CTX) throws Exception {// The ChannelPipeline is triggered after the channel registration is complete = ctx.pipeline(); boolean success =false; try { initChannel((C) ctx.channel()); ServerBootstrapAcceptor Pipeline.remove (this); ServerBootstrapAcceptor pipeline.remove(this); / / delete ChannelInitializer itself CTX. FireChannelRegistered (); // Continue passing the channel registration completion event success = along the pipetrue;
} catch (Throwable t) {
logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t);
} finally {
if(pipeline.context(this) ! = null) { pipeline.remove(this); }if(! success) { ctx.close(); }}}Copy the code
The partial class structure of the new NioServerSocketChannel is as follows:
- The unaddressed channel interface, for example, provides I/O operations for users, such as read, write, connect, and bind. The unaddressed channel interface, itself, does not expose data to users. In addition, it maintains a pipeline internally, which is used to connect various handlers to process data. In essence, a pipeline is a two-way linked list that maintains the relationship between handlers. It will hold the Channel reference of Netty itself, so that IO can be operated in the pipeline.
The channel() method is Netty's own channel
public DefaultChannelPipeline(AbstractChannel channel) {
if (channel == null) {
throw new NullPointerException("channel"); } this.channel = channel; //Netty's own channel tail = new TailContext(this); head = new HeadContext(this); head.next = tail; tail.prev = head; }Copy the code
- Another channel, the CH property held by AbstractNioChannel, is initialized by the JDK in NioServerSocketChannel, the ServerSocketChannel object. Netty internal
Any value retrieved from a javachannel() call is a JDK channel
, whereas unsafe itself performs register, bind, connect, write, and read operations through ServerSocketChannel
- Perform channel registration
- Perform channel binding
What conclusions can be drawn from the Hello World version of the server?
Netty NIO registers channels and binds listener ports through the JDK’s OWN NIO. How are select and channel used in Java NIO?
Attached custom handler code
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.printf("Server get:"+in.toString(CharsetUtil.UTF_8)); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { // Flush the message currently temporarily stored in the ChannelOutboundBuffer to the remote channel on the next flush or writeAndFlush and close the channel ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }}Copy the code
Netty client code hello world
public class EchoClient { private final String host; private final int port; public EchoClient(String host,int port){ this.host=host; this.port=port; } public void start() throws Exception{ EventLoopGroup group = new NioEventLoopGroup(); try{ Bootstrap b = new Bootstrap(); B.group (group).channel(niosocketchannel.class)// specify the NIO transport mode. RemoteAddress (new InetSocketAddress(host,port))// specify the remoteAddress Handler (new ChannelInitializer<SocketChannel>() {// Add handler @override protected void to channel pipeline initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new EchoClientHandler()); //channelHander to pipeline}}); ChannelFuture f = b.connect().sync(); // Connect to the remote node, block until connection is complete system.out.println ("wait"); f.channel().closeFuture().sync(); // block until connection is closed system.out.println ("over");
}finally {
System.out.println("shutdown"); group.shutdownGracefully().sync(); }} public static void main(String[]args) throws Exception{new EchoClient("localhost",8888).start(); }}Copy the code
As you can see from the code itself, the differences with server lie in the following two parts:
- Bootstrap: functions similar to ServerBootstrap. It uses the builder mode to build parameters required by the client, including the remoteAddress remoteAddress to be connected and a customized handler
- Conncet: Channel creation and registration are similar to those on the server side, The server adds a ServerBootstrapAcceptor and executes JDK connect after initializing the Channel
Note that no local address is actually specified here
Attached with custom handler code
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Hello world",CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("Client get:"+msg.toString(CharsetUtil.UTF_8)); }}Copy the code