package com.zhentao.information.handler; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * 心跳处理器 * 处理客户端的心跳检测 */ @Slf4j @Component @ChannelHandler.Sharable public class HeartbeatHandler extends ChannelInboundHandlerAdapter { private static final int MAX_MISSED_HEARTBEATS = 3; private int missedHeartbeats = 0; /** * 处理用户事件 * 当触发IdleStateEvent时调用 * @param ctx Channel上下文 * @param evt 事件对象 */ @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent) evt; if (event.state() == IdleState.READER_IDLE) { missedHeartbeats++; log.warn("读空闲,已错过 {} 次心跳", missedHeartbeats); if (missedHeartbeats >= MAX_MISSED_HEARTBEATS) { log.error("读空闲超时,关闭连接:{}", ctx.channel().id().asLongText()); ctx.close(); } else { // 发送心跳检测消息 ctx.writeAndFlush("ping"); } } else if (event.state() == IdleState.WRITER_IDLE) { // 发送心跳检测消息 ctx.writeAndFlush("ping"); } } else { super.userEventTriggered(ctx, evt); } } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof String && "pong".equals(msg)) { // 收到心跳响应,重置计数器 missedHeartbeats = 0; log.debug("收到心跳响应"); } else { super.channelRead(ctx, msg); } } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { log.info("连接断开:{}", ctx.channel().id().asLongText()); super.channelInactive(ctx); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { log.error("心跳处理器异常", cause); ctx.close(); } }