《Netty学习打卡--从小白到放弃》----- 06 - netty读写检测机制(心跳机制)

打卡日期(2019-07-05)

netty 读写检测机制(心跳机制)

学习要点
-   1.什么是心跳机制?
-   2.IdleStateHandler 
1.什么是心跳机制

    所谓心跳,即在TCP长链接中,客户端跟服务器端之间定期发送一种特殊的数据包(心跳包),通知对方自己还在线,以确保TCP链接的有效性。
     注:心跳包还有另一个作用经常被忽略,即:一个连接如果长时间不用,防火墙或者路由器就会断开该链接

2.IdleStateHandler

    netty自带的心跳监测实例,当连接的空闲时间(读或者写)太长时候,就会触发一个IdleStateEvent事件,然后服务端添加IdleStateHandler心跳监测处理器,并添加自定义处理器Handler类实现userEventTriggered()方法作为超时时间逻辑处理。
    IdleStateHandler的三个实例化参数

  • readerIdleTime:读空闲超时时间设置,如果channelRead0()方法超时readerIdleTime时间违背调用则会触发读超时事件,调用userEventTriggered()
  • writerIdleTime:写超时时间设置,如果服务器向客户端在规定的writerIdleTime时间内没有写入数据,则会触发写超时事件,调用userEventTriggered()方法
  • allIdleTimeSeconds:读写超时事件,所有类型的空闲时间超时设定,包括读和写
  • unit:时间单位,默认为秒

服务端

package com.dragon.heart;

import com.dragon.netty.InitServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

/**
 * @Description:
 */
public class MyHeartBeatServer {
    public static void main(String[] args) throws InterruptedException {

        //事件循环组 acceptorGroup用于接收请求
        EventLoopGroup acceptorGroup = new NioEventLoopGroup();

        //事件循环组 handlerGroup用于处理请求
        EventLoopGroup handlerGroup = new NioEventLoopGroup();
        try{
            // sub-class which allows easy bootstrap of  用于轻松的启动bootstrap
            ServerBootstrap serverBootstrap = new ServerBootstrap()
                    // group (acceptorGroup,handlerGroup)
                    // acceptorGroup  用于接收所有的请求
                    // handlerGroup  处理所有的事件和IO操作
                    .group(acceptorGroup,handlerGroup)
                    // NioSctpServerChannel 利用nio模式接收一个新的连接创建NioSctpChannel
                    .channel(NioServerSocketChannel.class)
                    // 服务端设置handler,只会对acceptorGroup 事件组生效
                    .handler(new LoggingHandler(LogLevel.INFO))
                    // 自定义处理器
                    .childHandler(new MyHeartBeatServerInit());
            //绑定80端口,端口号可以自定义
            ChannelFuture future = serverBootstrap.bind(8080).sync();
            future.channel().closeFuture().sync();
        }finally {
            //友好关闭两个线程组
            acceptorGroup.shutdownGracefully();
            handlerGroup.shutdownGracefully();
        }

    }
}
这里需要备注一下:  
serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)),
这个日志处理器只会作用于服务端
serverBootstrap.handler()这个方法只针对服务端添加事件处理器
package com.dragon.heart;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;


/**
 * @Description:  初始化器
 */
public class MyHeartBeatServerInit extends ChannelInitializer<SocketChannel>{

    /**
     *  一旦channel管道被注册这个方法就会被调用,这个方法返回一个之后实例将会从ChannelPipeline移除channel
     */
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        // 在次SocketChannel管道的位置增追加一个通道处理器
        ch.pipeline().addLast("idleStateHandler",new IdleStateHandler(8,8,10, TimeUnit.SECONDS));
        // 在管道的最后一个位置追加自己的通道处理器
        ch.pipeline().addLast("myHeartBeatHandler",new MyHeartBeatHandler());
    }
}
package com.dragon.heart;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;

/**
 * @Description:
 */
public class MyHeartBeatHandler extends SimpleChannelInboundHandler<Object> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {

    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        Channel channel = ctx.channel();
        String eventType = null;
        if(evt instanceof IdleStateEvent){
            IdleStateEvent event = (IdleStateEvent) evt;
            switch (event.state()){
                case READER_IDLE:
                    eventType = "读事件";
                    break;
                case WRITER_IDLE:
                    eventType = "写事件";
                    break;
                case ALL_IDLE:
                    eventType = "读写事件";
                    break;
            }
        }
        System.out.println("【服务器】 "+ channel.remoteAddress() +","+ eventType + " 超时");
    }
}

客户端

package com.dragon.chart.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @Description: 客户端
 */
public class MyChartClient {

    public static void main(String[] args) throws InterruptedException, IOException {
        EventLoopGroup clientGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        try{
            bootstrap.group(clientGroup).channel(NioSocketChannel.class).handler(new MyChartClientInit());
            Channel channel = bootstrap.connect("localhost",8080).sync().channel();

            // 获取键盘输入的事件,通过通道channel将获取的内容写进通道
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            for(;;){
                channel.writeAndFlush(br.readLine() + "\r\n");
            }
        }finally {
            clientGroup.shutdownGracefully();
        }
    }
}
package com.dragon.chart.client;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

/**
 * @Description:
 */
public class MyChartClientInit  extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        /***
         * @Param maxFrameLength 解帧码的最大长度
         * @Param delimiters   分隔符
         */
        pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));

        /***
         * 解码器处理器
         */
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));

        /***
         * 编码器处理器
         */
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));

        /***
         * 自定义处理器
         */
        pipeline.addLast(new MyChartClientHandler());
    }
}
package com.dragon.chart.client;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * @Description:
 */
public class MyChartClientHandler extends SimpleChannelInboundHandler<String> {


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        /***
         * 客户端不需要处理信息内容,能查看就行,所以不做任何处理,打印出来即可
         */
        System.out.println(msg);
    }
}

注:客户端着三个类全部采用子上一章节的代码
[《Netty学习打卡--从小白到放弃》----- 05 - netty实现简单的聊天功能](https://blog.csdn.net/u011291990/article/details/94722092)

分别运行服务端和客户端的代码,在客户端不输入任何信息的情况下会出现如下日志:


七月 05, 2019 3:43:22 下午 io.netty.handler.logging.LoggingHandler channelRegistered
信息: [id: 0x21f153de] REGISTERED
七月 05, 2019 3:43:22 下午 io.netty.handler.logging.LoggingHandler bind
信息: [id: 0x21f153de] BIND: 0.0.0.0/0.0.0.0:8080
七月 05, 2019 3:43:22 下午 io.netty.handler.logging.LoggingHandler channelActive
信息: [id: 0x21f153de, L:/0:0:0:0:0:0:0:0:8080] ACTIVE
七月 05, 2019 3:43:29 下午 io.netty.handler.logging.LoggingHandler channelRead
信息: [id: 0x21f153de, L:/0:0:0:0:0:0:0:0:8080] READ: [id: 0xa93b9f73, L:/127.0.0.1:8080 - R:/127.0.0.1:9786]
七月 05, 2019 3:43:29 下午 io.netty.handler.logging.LoggingHandler channelReadComplete
信息: [id: 0x21f153de, L:/0:0:0:0:0:0:0:0:8080] READ COMPLETE
【服务器】 /127.0.0.1:9786,读事件 超时
【服务器】 /127.0.0.1:9786,写事件 超时
【服务器】 /127.0.0.1:9786,读写事件 超时
【服务器】 /127.0.0.1:9786,读事件 超时
【服务器】 /127.0.0.1:9786,写事件 超时
【服务器】 /127.0.0.1:9786,写事件 超时
【服务器】 /127.0.0.1:9786,读事件 超时
【服务器】 /127.0.0.1:9786,读写事件 超时
【服务器】 /127.0.0.1:9786,写事件 超时
【服务器】 /127.0.0.1:9786,读事件 超时
通过代码可以看到,netty利用IdleStateHandler这个处理器完成了一个简单的心跳检测机制,具体在真实场景中如何使用,后续博客体现
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值