IdleStateHandler 简介#
IdleStateHandler 的本质上也是一个 channelhandler,也就是说可以把他加到 pipeline 里边。
IdleStateHandler 的作用就是在某些情况下的触发一个 IdleStateEvent。
某些情况:
在一段时间内
- 没有读
- 没有写
- 没有读写
在触发 IdleStateEvent 之后就可以被下游的的 handler 接收到。
比如 10 秒没有读操作,发出一个READER_IDLE
ch.pipeline()
.addLast(new IdleStateHandler(10, 0, 0, TimeUnit.SECONDS))
.addLast(new HeartTrigger())
}
});
位于下游的HeartTrigger
就可以收到这个事件
public class HeartTrigger extends ChannelInboundHandlerAdapter {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
// do something
} else {
super.userEventTriggered(ctx, evt);
}
}
}
心跳#
心跳的模型很简单,客户端和服务端创建连接后,使用 IdleStateHandler 监听写操作,
如果在一定的时间内没有写* 操作,主动发送一个心跳包造成写操作。
客户端的写
操作对应着服务端的读
操作。也就是说客户端的心跳包或造成服务端的读操作。
而服务端监听 channel 的读操作,如过超时没有读操作,将关闭 channel(连接)。
而服务端的读监听时间间隔要比客户端的写监听时间间隔稍长一点,不然会因为略微的延迟而关闭 channel。
相关代码#
心跳包是自定义的数据结构,要考虑可能存在的半包 / 粘包问题
客户端发送心跳包
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
if (((IdleStateEvent) evt).state() == IdleState.WRITER_IDLE) {
logger.info("发个心跳包告诉服务器自己没死。。");
ctx.writeAndFlush(new TransData.Builder()
.type(TransData.TYPE_HT)
.build());
}
}
}
服务端长时间没收到客户都的东西就关了 channel
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
if (((IdleStateEvent) evt).state() == IdleState.READER_IDLE) {
logger.info("过久没收到客户端心跳,断开连接");
ctx.close();
}
} else {
super.userEventTriggered(ctx, evt);
}
}