Netty(二):编解码器和心跳机制

在上一篇文章中讲了底层的NIO模型,并且写了一个netty的简单demo,这篇文章主要讲netty中一些具体的应用。

编解码器

首先需要了解netty中pipeline。

在这里插入图片描述

  • 对于pipeline,有入站和出站的概念,以服务端举例,从客户端发送来数据就是入站,就是上图从head到tail。
  • 对于每一个handler,我们在添加入管道的时候,都是使用addLast方法,可以看做直接加入tail的前一个。
    在这里插入图片描述
  • 自定义的handler都是实现了InboundHandler(入站)或者是OutboundHandler(出站)。然后对于入站事件只会执行入站的handler,出站事件只会执行出站的handler。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在网络传输中,数据是以字节数组的方式进行传输的(或者说是二进制数据),因此在我们自定义的handler中进行数据传输,Object类型都需要编码成byte数组,存到netty的操作ByteBuf中才能进行传输。

netty自带的编码器是基础jdk自带的序列化机制实现的,下面是展示String和对象的传输的demo。

public class NettyServer {

    public static void main(String[] args) throws Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new StringDecoder());
                            //pipeline.addLast(new ObjectDecoder(10240, ClassResolvers.cacheDisabled(ClassLoader.getSystemClassLoader())));
                            pipeline.addLast(new NettyServerHandler());
                        }
                    });
            System.out.println("netty server start...");
            ChannelFuture cf = bootstrap.bind(9000).sync();
            cf.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        System.out.println("从客户端读取的String:" + msg.toString());
//        System.out.println("从客户端读取的Object:" + ((User)msg).toString());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

public class NettyClient {

    public static void main(String[] args) throws Exception{

        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group) //设置线程组
                    .channel(NioSocketChannel.class) 
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {

                            ChannelPipeline pipeline = channel.pipeline();
                            pipeline.addLast(new StringEncoder());
                            //pipeline.addLast(new ObjectEncoder());
                            pipeline.addLast(new NettyClientHandler());
                        }
                    });

            System.out.println("netty client start..");
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1",9000).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("MyChannelHandler 发送数据");
       ctx.writeAndFlush("测试String编解码");
        //测试对象编解码
       // ctx.writeAndFlush(new User(1,"xin"));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        super.channelRead(ctx, msg);
    }
}

在这里插入图片描述

StringDecoder实际上就是个InboundHandler。
可以使用protostuff进行序列化操作,这样就可以自定义编解码器。

心跳机制

所谓心跳机制,就是客户端和服务端发送一些数据包,告诉对方在线,来确保连接的有效性。

public class HeartBeatServer {

    public static void main(String[] args) throws Exception{

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workerGroup) //设置线程组
                    .channel(NioServerSocketChannel.class) // 使用 NioSocketChannel 作为客户端的通道实现
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            ChannelPipeline pipeline = channel.pipeline();
                            pipeline.addLast(new StringEncoder());
                            pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new IdleStateHandler(3,0,0, TimeUnit.SECONDS));
                            pipeline.addLast(new HeartBeatServerHandler());
                        }
                    });

            System.out.println("netty server start..");
            ChannelFuture cf = bootstrap.bind(9000).sync();
            cf.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

public class HeartBeatServerHandler extends ChannelInboundHandlerAdapter {

    int readIdleTimes = 0;

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        IdleStateEvent event = (IdleStateEvent) evt;

        String type = "读未超时";
        if (event.state() == IdleState.READER_IDLE) {
            readIdleTimes++;
            type = "读超时";
        }
        System.out.println(ctx.channel().remoteAddress() + "  "+ type);
        if (readIdleTimes > 3) {
            //超过3次断开连接
            System.out.println("超过3次断开连接");
            ctx.writeAndFlush("connection close");
            ctx.channel().close();
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+"  is alive..........");
    }
    
}

public class HeartBeatClient {

    public static void main(String[] args) throws Exception {

        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group) //设置线程组
                    .channel(NioSocketChannel.class) // 使用 NioSocketChannel 作为客户端的通道实现
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {

                            ChannelPipeline pipeline = channel.pipeline();
                            pipeline.addLast(new StringEncoder());
                            pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new HeartBeatClientHandler());
                        }
                    });

            System.out.println("netty client start..");
            Channel channel = bootstrap.connect("127.0.0.1", 9000).sync().channel();
            String text = "HeartBeat";
            Random random = new Random();
            while (channel.isActive()) {
                int num = random.nextInt(10);
                Thread.sleep(num * 1000);
                channel.writeAndFlush(text);
            }
        } finally {
            group.shutdownGracefully();
        }
    }
}

public class HeartBeatClientHandler extends SimpleChannelInboundHandler<String> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
        System.out.println(s.trim());
        if (s.equals("connection close")) {
            System.out.println("服务端关闭连接,因此关闭客户端");
            ctx.channel().close();
        }
    }
}

实现心跳机制需要IdleStateHandler,下面进入源码看下这个类怎么实现功能
在这里插入图片描述
进入initialize方法
在这里插入图片描述
这里面执行了一个schedule,里面有个task,进入task看看
在这里插入图片描述
看下他的run方法
在这里插入图片描述
在这里插入图片描述
如果超时,就会执行红色的部分,然后下面继续执行fireUserEventTriggered方法,这个方法是调用下一个handler的UserEventTriggered方法,即我们自定义的handler中的userEventTriggered方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值