美文网首页
Netty 之 IdleStateHandler 心跳检测(部分

Netty 之 IdleStateHandler 心跳检测(部分

作者: 程就人生 | 来源:发表于2021-01-21 19:21 被阅读0次

    问题描述:之前写过使用 Netty 框架搭建的 TCP 服务器,但是发现一个问题,客户端明明有心跳信息发送(5s发送一次心跳),为什么服务器端没有继续保持心跳(检测时间50s),而总是会断开连接呢,于是把心跳处理的Handler撸了一遍。

    客户端打印日志

    服务器端打印日志 错误代码如图所示

    在 Netty 中,实现心跳机制的关键是 IdleStateHandler(空闲状态处理器)类,这里继承了 IdleStateHandler 类。

    先看看 IdleStateHandler 类吧。在该类上面有一个示例demo,这个demo的意思是,先设置 IdleStateHandler 处理器,然后在随后的handler处理?

    接下来把心跳处理的handler改了一遍,改过的代码如下图所示:

    /**
     * 心跳处理
     * @author 关注微信公众号:程就人生,获取更多源码
     * @date 2021年1月14日
     * @Description 
     *
     */
    public class HeartBeatServerHandler extends ChannelInboundHandlerAdapter{
    
      private static Logger log = LoggerFactory.getLogger(HeartBeatServerHandler.class);
      
      //设置检测时间为50s
      public static final int READ_IDLE_GAP = 50;
    
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {    
        // 判断消息实例
        if (null == msg || !(msg instanceof byte[])) {
          super.channelRead(ctx, msg);
          return;
        }
        byte[] data = (byte[]) msg;
            int dataLength = data.length;
            ByteBuf buf = Unpooled.buffer(dataLength);
            buf.writeBytes(data);
            int type = CharacterConvert.byteToInt(buf.readByte());  
            //机器编号
            int deviceId = CharacterConvert.byteToInt(buf.readByte());
            //如果是管理后台登录操作时
            if(type == ProtoInstant.HEART_BEAT){  
              int verify = CharacterConvert.byteToInt(buf.readByte());
              buf.retain();
              int sum = CharacterConvert.sum(ProtoInstant.FIELD_HEAD, dataLength + ProtoInstant.FILED_LEN, type, deviceId);
              if(verify != CharacterConvert.getLow8(sum)){
                log.error("心跳包,校验位错误!机器编码:" + deviceId);
              }else{
                log.info("接收到心跳信息" + deviceId);  
                if (ctx.channel().isActive()) {
                ctx.writeAndFlush(msg);
              }
              }    
        }else{
          super.channelRead(ctx, msg);
        }    
      }
      
      /**
      * 用户事件触发
      * 当 IdleStateHandler发现读超时后,会调用 fireUserEventTriggered()去执行后一个 Handler的 userEventTriggered方法
      * 所以这里需要重写 userEventTriggered 方法
      */
      @Override
      public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
         if (evt instanceof IdleStateEvent) {
           IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
             if (idleStateEvent.state() == IdleState.READER_IDLE) {
               log.error("超过" + READ_IDLE_GAP + "s未收到客户端连接,连接执行关闭操作~!");
                 ctx.close();
           }
          }
      }
    }
    

    第一处改动:继承类的改动;

    第二处改动:重写userEventTriggered方法;

    第三次改动:把服务器初始化channel的地方改动了一下:

    重启服务端,客户端依旧保持每5s发送一次心跳的频率,服务器端50s检测一次,运行结果如下图所示,没有再出现刚开始时的错误信息。

    服务器端运行结果

    再来一次测试,把客户端发送的频率改为55s,看看服务器端如何反应,服务器端50s内没收到心跳信息,连接关闭,客户端只能重连。

    服务器端控制台输出 客户端控制台输出

    功能实现了,但还是有必要再研究一下源码,IdleStateHandler 还有两个子类:ReadTimeoutHandler、WriteTimeoutHandler,分别针对读超时和写超时操作的,用法和IdleStateHandler的用法是一样的,只不过两个子类在超时后直接报超时异常。

    IdleStateHandler的继承关系 ReadTimeoutHandler超时检测

    这里只分析到这里,更多源码请自行查看,也可参考下文分析,感谢网友的辛勤付出:
    https://blog.csdn.net/weixin_43935927/article/details/112001309

    相关文章

      网友评论

          本文标题:Netty 之 IdleStateHandler 心跳检测(部分

          本文链接:https://www.haomeiwen.com/subject/dbvdzktx.html