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);
}
}