Netty 之 IdleStateHandler 心跳检测(部分源码分析),实现超时断开连接

问题描述:之前写过使用 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 心跳检测(部分源码分析),实现超时断开连接)