问题描述:之前写过使用 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
网友评论