Netty 4 实践 (1)初识netty4

netty 5正式版还没有出,项目准备从netty 3升级到netty4。

mina与netty 是同一个人写的,mina将内核和一些特性的联系过于紧密,使得用户在不需要这些特性的时候无法脱离,相比下性能会有所下降;netty解决了这个设计问题,按照这个理论netty 是mina 的升级。

从netty3,到netty4的代码变话还是挺多的,首先看看bootstrap,它的结构可以说比较简单,涉及的类和接口很少;如图:

Bootstrap则是客户端程序用的引导类,ServerBootstrap是服务端程序用的引导类,Bootstrap 和ServerBootstrap都继承自AbstractBootstrap,ChannelFactory用于处理channel。

      ChannelFuture被拆分为ChannelFuture和ChannelPromise。这不仅仅是让异步操作里的生产者和消费者间的约定更明显,同样也是得在使用从链中返回的ChannelFuture更加安全,因为ChannelFuture的状态是不能改变的。
由于这个编号,一些方法现在都采用ChannelPromiser而不是ChannelFuture来改变它的状态。

      在编码解码器框架里有实质性的内部改变,因为4.0需要一个处理器来创建和管理它的缓存(看这篇文章的每个处理器缓存部分。)然而,从用户角度来看这些变化都不是很大的。核心编码界面器类移到io.netty.handler.codec包里。FrameDecoder重命名为ByteToMessageDecoder。OneToOneEncoder和OneToOneDecoder由MessageToMessageEncoder和MessageToMessageDecoder替换。

       看了看官网netty 4的入门实践 http://netty.io/wiki/user-guide-for-4.x.html 写了个例子,体验和一步一步来研究一下netty 4

   首先来写服务端:

public class ByteServer {

    private final static String host = "127.0.0.1";
    private final static Integer port = 8898;

    public static void main(String[] args) {
        /***
         * ·NioEventLoopGroup 实际上是个连接池,NioEventLoopGroup在后台启动了n个NioEventLoop
         * 来处理Channel事件,每个NioEventLoop负责m个Channel
         * ·NioEventLoopGroup从NioEventLoop数组集中挨个取出NioEventLoop用以处理Channel
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup);
            // 设置 nio 类型的 channel
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.childHandler(new ByteServerInitializer());
            //此处在写一个TCP/IP 的服务端,因此我们被允许设置 socket 的参数选项比如tcpNoDelay 和 keepAlive。
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
            /***
             * option() 是提供NioServerSocketChannel用来接收进来的连接。
             * childOption() 是提供父管道ServerChannel接收到的
             * 连接(此例是 NioServerSocketChannel)。
             */
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            // 服务器绑定端口监听(sync,同步方法阻塞直到绑定成功)
            ChannelFuture channelFuture = serverBootstrap.bind(host, port).sync();
            // 监听服务器关闭监听(应用程序等待直到channel关闭)
            channelFuture.channel().closeFuture().sync();

            // logger.info("TCP服务器已启动");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //关闭EventLoopGroup释放资源包
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
是不是感觉比netty 3 的代码变化了好多,接下来写ByteServerInitializer类:

public class ByteServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        /***
         * 个地方的 必须和服务端对应上。否则无法正常解码和编码
         */
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 8, 0, 8));
        pipeline.addLast(new LengthFieldPrepender(8));
        //当 read 的时候
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        //当 send 的时候
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        //服务端逻辑
        pipeline.addLast(new ByteServerHandler());
    }
}
接下来写服务端handler 类,处理数据:

public class ByteServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (null != msg) {
//            ByteBuf byteBuf = ((ByteBuf) msg);
            StringBuffer sbf = new StringBuffer("收到客户端->");
            sbf.append(ctx.channel().remoteAddress());
            sbf.append("的消息:");
            sbf.append(msg);
            System.out.println(sbf);
            ctx.writeAndFlush("ok");
//            byteBuf.release();
            ctx.close();
        }
    }

    /***
     * 覆盖了 channelActive() 事件处理方法。服务端监听到客户端活动
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive>>>> Client:" + ctx.channel().remoteAddress() + "在线");
        ctx.fireChannelActive();
    }

    /***
     * 监听客户端掉线
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // System.out.println("channelInactive>>>> Client:"+ctx.channel().remoteAddress()+"掉线");
        super.channelInactive(ctx);
    }

    /*****
     * 异常信息 (根据需要,选择是否关闭)
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("server exception is general");
         ctx.fireExceptionCaught(cause);
//        ctx.close();
    }
}

客户端

public class ByteClient {
    private final static String host = "127.0.0.1";
    private final static Integer port = 8898;

    public static void main(String[] args) {
        EventLoopGroup group = null;
        try {
            group = new NioEventLoopGroup();
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group);
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.handler(new ByteClientInitializer());
            bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
            Channel ch = bootstrap.connect(host, port).sync().channel();
            ch.writeAndFlush("发送一条指令:我的小鱼你醒了,还认识早晨吗?" + Thread.currentThread().getName());
            ch.closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
           if(null!=group){
               group.shutdownGracefully();
           }
        }
    }
}

public class ByteClientInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        /****
         * 个地方的 必须和服务端对应上。否则无法正常解码和编码
         */
        pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 8, 0, 8));
        pipeline.addLast("frameEncoder", new LengthFieldPrepender(8));
        //当 read 的时候
        pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
        //当 send 的时候
        pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
        // 客户端的逻辑
        pipeline.addLast("handler", new ByteClientHander());
    }
}

public class ByteClientHander extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(null!=msg){
            try {
               System.out.println("服务端返回消息:" + msg);
            }finally {
                ReferenceCountUtil.release(msg);
            }
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }

    /***
     * 异常处理
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("client exception is general");
        /***
         * 异常信息
         *   cause.printStackTrace();
         */
        ctx.fireExceptionCaught(cause);
    }
}
客户端向服务端,发数据服务端如果返回OK则表示数据发送成功,没有包含其他逻辑;一个简单的例子就成了。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xxpp688

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值