Netty入门应用之第一个netty项目

服务器端代码

public class TimeServer {
    public void bind(int port) throws Exception{
        //创建两NioEventLoopGroup实例。
        //NioEventLoopGroup是个线程组。
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{
            //创建ServerBootstrap对象,它是Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度。
            ServerBootstrap b = new ServerBootstrap();
            //调用ServerBootstrap的group方法,将两个NIO线程组当做入参传递到ServerBootstrap中。
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new TimeServerHandler());
                        }
                    });
            ChannelFuture f = b.bind(port).sync();
            //等待服务锻炼路关闭之后main函数才退出。
            f.channel().closeFuture().sync();
        }finally {
            //优雅退出,释放线程池资源。
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception{
        int port = 8088;
        new TimeServer().bind(port);
    }
}
Netty服务器端Handler端代码

public class TimeServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //类型转换:将msg转成ByteBuf对象
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req , "UTF-8");
        System.out.println("The time server receive order:" + body);
        String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(
                System.currentTimeMillis()).toString() : "BAD ORDER" ;
        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
        ctx.write(resp);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // TODO Auto-generated method stub
        super.channelReadComplete(ctx);
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // TODO Auto-generated method stub
        super.exceptionCaught(ctx, cause);
        ctx.close();
    }
}
先对服务器端的代码进行一下解析:

        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
创建两个NioEventLoopGroup实例,NioEventLoopGroup是一个线程组,专门用于网络事件的处理。

这里创建两个的原因是一个用于服务器端接受客户端的连接,另一个用于进行SocketChannel的网络读写。


       ServerBootstrap b = new ServerBootstrap();
创建ServerBootstrap用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度。


 b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new TimeServerHandler());
                        }
                    });
调用ServerBootstrap的group方法将两个NIO线程组传递到ServerBootstrap中,设置创建的channel为NioSeverSocketChannel,功能是对应JDK NIO类库中的ServerSocketChannel类。然后配置NioServerSocektChannel的TCP参数。最后绑定处理类,主要用于处理网络I/O事件。


 f.channel().closeFuture().sync();
阻塞方法,等待服务端链路关闭之后main函数才退出。

            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
优雅退出,释放相关资源。

        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req , "UTF-8");
将msg转换成netty的ByteBuf对象。通过ByteBuf的readableBytes方法可以获取缓冲区可读字节数,根据可读字节数创建byte数组,通过ByteBuf的readBytes方法将缓冲区中的字节数组复制到新建的byte数组中。

    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // TODO Auto-generated method stub
        super.exceptionCaught(ctx, cause);
        ctx.close();
    }
当发生异常时,关闭ChannelHandlerContext,释放和channelHandlerContext相关联的句柄资源。


Netty客户端TimeClient

 
  
public class TimeClient {
    public void connent(int port , String host) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try{
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer() {

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new TimeClientHandler());
                        }
                    });
            ChannelFuture f = b.connect(host , port).sync();

            f.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully();
        }
    }
    public static void main(String[] args) throws Exception{
        int port = 8088;
        new TimeClient().connent(port, "127.0.0.1");
    }
}

Netty客户端TimeClientHandler 代码

public class TimeClientHandler extends ChannelInboundHandlerAdapter {
    private static final Logger logger = Logger.getLogger(TimeClientHandler.class.getName());

    private byte[] req ;

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        this.req = "QUERY TIME ORDER".getBytes();
        ByteBuf message = Unpooled.buffer(req.length);
        message.writeBytes(req);
        ctx.writeAndFlush(message);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req , "UTF-8");
        System.out.println("Now is : "+ body);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.warning("Unexpected exception from downstream : "+cause.getMessage());
        ctx.close();
    }
}
重点在channelActive, channelRead 和 execptionCaught 三个方法上。当客户端和服务器端TCP链路建立成功之后,Netty的NIO线程会调用channelActive方法,发送查询指令给服务器端,调用ChannelHandlerContext的writeAndFlush方法将请求消息发送给客户端。 当服务端返回应答时,channelRead方法被调用。


你可能感兴趣的:(技术,netty,服务器,nio)