实际情况下客户端断开与服务端连接的方式有很多,例如开启飞行模式、突然的无网络服务,这类情况下的连接断开无法使用 handlerRemoved 捕获到,因此需要在服务端使用心跳检测机制来确保服务端的准确下线
可以使用IdleStateHandler进行心跳检测,心跳检测分为三种,分别是:没有读操作、没有写操作、没有读写操作,当这些情况发生后,IdleStateHandler将会触发事件给下一个Handler进行处理
代码:
心跳检测服务端代码:
package keepalive;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.*;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.logging.LogLevel;import io.netty.handler.logging.LoggingHandler;import io.netty.handler.timeout.IdleStateHandler;import java.util.concurrent.TimeUnit;/* 心跳检测Handler案例 */public class MyServer {public static void main(String[] args) throws InterruptedException {EventLoopGroup bossGroup = new NioEventLoopGroup(1); //管理连接请求线程组EventLoopGroup workGroup = new NioEventLoopGroup(); //进行业务处理线程组try {ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.group(bossGroup,workGroup) //设置两个线程组.channel(NioServerSocketChannel.class) //使用 NioSocketChannel 作为服务器通道实现.handler(new LoggingHandler(LogLevel.INFO)) //在BossGroup添加一个日志处理器.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();/*Netty提供的IdleStateHandler(处理空闲状态的处理器),实际的客户端离线情况并不是都会被 handlerRemoved 捕获到* 只有心跳检测才能准确确认客户端离线的准确性,符合参数对应的事件发生后将会触发 IdlestateEvent* readerIdleTime: 没有读操作的时间,到时间会发送一个心跳检测包检测是否连接* writerIdleTime: 没有写操作的时间,到时间会发送一个心跳检测包检测是否连接* allIdleTime: 没有读写操作的时间,到时间会发送一个心跳检测包检测是否连接* TimeUnit: 时间格式*/pipeline.addLast(new IdleStateHandler(3,5,7, TimeUnit.SECONDS));/** 当 IdlestateEvent 触发后,就会传递给管道的下一个handler* 通过调用(触发)下一个handler的 userEventTiggered,在该方法中去处理 IdlestateEvent* 所以需要自定义一个Handler放在IdleStateHandler的后面*/pipeline.addLast(new MyServerHandler()); //添加空闲检测进一步处理handler}});ChannelFuture future = serverBootstrap.bind(7000).sync();future.channel().closeFuture().sync(); //对关闭通道进行监听(当有关闭通道的消息时才进行监听)} finally {bossGroup.shutdownGracefully();workGroup.shutdownGracefully();}}}
心跳检测自定义Handler:
package keepalive;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.handler.timeout.IdleStateEvent;/* 心跳检测自定义处理Handler */public class MyServerHandler extends ChannelInboundHandlerAdapter {/** ctx: 上下文* evt: 事件*/@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if(evt instanceof IdleStateEvent){IdleStateEvent event = (IdleStateEvent) evt;String eventType = null;switch (event.state()){case READER_IDLE:eventType = "读空闲(客户端没有发送消息)";break;case WRITER_IDLE:eventType = "写空闲(服务端没有数据返回给客户端)";break;case ALL_IDLE:eventType = "读写空闲(客户端和服务端都没操作)";ctx.channel().close();break;default: break;}System.out.println(ctx.channel().remoteAddress()+"--超时时间--"+eventType);System.out.println("服务器需要做出处理....");}}}
测试:
启动心跳检测服务端后再启动之前的聊天室客户端代码,可以看到在指定的allIdleTime时间后客户端由于没有做任何操作触发了 ALL_IDLE 事件,通道被关闭
客户端通道长时间闲置,在指定时间后被关闭
