It's a web-socket application based on netty. However, when the client is physically killed without sending a CloseWebSocketFrame
, server needs to know the channel has been closed and do some cleaning work. IdleStateHandler
is used to monitor whether the channel is idle.
Here is the channel handler pipeline.
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
SSLEngine sslEngine = new SSLServiceBase().getSSLContext().createSSLEngine();
sslEngine.setUseClientMode(false);
sslEngine.setNeedClientAuth(true);
... // business process
pipeline.addLast(new IdleStateHandler(30, 10, 0, TimeUnit.SECONDS));
pipeline.addLast(new HeartbeatHandler());
}
This is the userEventTriggered
method of HeartbeatHandler
which is used to do some cleaning work when channel is closed from the client abnormally.
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (IdleState.WRITER_IDLE.equals(event.state())) {
ctx.writeAndFlush(new PingWebSocketFrame()).addListener(future -> {
if (!future.isSuccess()) {
ChannelFuture closeFuture = ctx.channel().close();
if (closeFuture.isSuccess()) {
System.out.println("ping faild, channel close successfully");
} else {
System.out.println("ping failed, channel close failed");
}
} else {
System.out.println("Ping succeed, keep the channel.");
}
});
}
} else {
super.userEventTriggered(ctx, evt);
}
}
Actually, I'm continuously getting 'close failed'
, and the channel is still alive from the server view. Could anyone let me know why the channel can't be closed or how to close? Thanks a lot.
I suspect the close was not done yet (remember it is an async operation). Change the code to:
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (IdleState.WRITER_IDLE.equals(event.state())) {
ctx.writeAndFlush(new PingWebSocketFrame()).addListener(future -> {
if (!future.isSuccess()) {
ctx.close().addListener(closeFuture -> {
If (closeFuture.isSuccess()) {
System.out.println("ping faild, channel close successfully");
} else {
System.out.println("ping failed, channel close failed");
// TODO: You may also want to log the reason why the close operation failed.
// closeFuture.cause() will contain why.
}
});
} else {
System.out.println("Ping succeed, keep the channel.");
}
});
}
} else {
super.userEventTriggered(ctx, evt);
}
}