io.netty
netty-all
4.1.5.Final
junit
junit
4.10
test
该类构造方法为:
public IdleStateHandler(long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit)
readerIdleTime代表着读的空闲时间,而writerIdleTime表示写的空闲时间,TimeUnit表示时间的单位
当超过读的时间(会一直监听channelRead方法)或者写的时间(监听有无写入)时,会调用入站ChannelInboundHandlerAdapter类中的userEventTriggered方法,只要在该方法里判断引起该方法的事件就可以来进行逻辑的处理,比如,引起读的事件:
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if(evt instanceof IdleStateEvent){
//服务端对应着读事件,当为READER_IDLE时触发
IdleStateEvent event = (IdleStateEvent)evt;
if(event.state() == IdleState.READER_IDLE){
.......
}
}else{
super.userEventTriggered(ctx,evt);
}
}
}
了解到这个类后原理很简单,可以在客户端加入IdleStateHandler类处理,当客户端超过设置的写入时间时,会调用userEventTriggered方法,在里面判断是否是出发写事件,将服务端写入数据,服务端也原理一样
重连作用于某个时刻服务端断掉,或者客户端断掉后自动进行重新连接的操作。上述是一个需要重连的场景,还有一个场景就是第一次连接服务器不成功,需要再次尝试的情况下。
针对第一种情况,基于Netty的机制,我们可以在channelInactive方法里,进行重连的操作,因为该方法是在Channel关闭时调用的。
针对第二种情况,我们可以在客户端连接服务端的时候加入监听,当连接不成功的情况下,再次进行连接。
下面的Demo是在网上现成的代码下修改的,客户端会向服务端发送三次消息,服务端接收到三次消息后会把客户端给关闭,客户端则触发重新连接的操作,将在10后进行重连的操作
public class HeartBeatServerHandler extends ChannelInboundHandlerAdapter {
private int loss_connect_time = 0;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(ctx.channel().remoteAddress() + "Server :" + msg.toString());
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if(evt instanceof IdleStateEvent){
//服务端对应着读事件,当为READER_IDLE时触发
IdleStateEvent event = (IdleStateEvent)evt;
if(event.state() == IdleState.READER_IDLE){
loss_connect_time++;
if(loss_connect_time > 2){
System.out.println("接收消息超时");
System.out.println("关闭不活动的链接");
ctx.channel().close();
}
}else{
super.userEventTriggered(ctx,evt);
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
public class HeartBeatServer {
private int port;
public HeartBeatServer(int port) {
this.port = port;
}
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap server = new ServerBootstrap().group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("handler",new IdleStateHandler(4, 0, 0, TimeUnit.SECONDS));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast(new HeartBeatServerHandler());
}
}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);
try {
ChannelFuture future = server.bind(port).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
public class HeartBeatClientHandler extends ChannelInboundHandlerAdapter {
private HeartBeatsClient heartBeatsClient;
private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Heartbeat",
CharsetUtil.UTF_8));
private static final int TRY_TIMES = 3;
private int currentTime = 1;
public HeartBeatClientHandler(HeartBeatsClient heartBeatsClient){
this.heartBeatsClient=heartBeatsClient;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("激活时间是:"+new Date());
System.out.println("链接已经激活");
ctx.fireChannelActive();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("停止时间是:"+new Date());
System.out.println("关闭链接");
System.out.println("将进行重连");
final EventLoop loop = ctx.channel().eventLoop();
loop.schedule(new Runnable() {
public void run() {
try {
System.out.println("lalallal");
heartBeatsClient.reConnectServer(new Bootstrap(),loop.parent());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},10L,TimeUnit.SECONDS);
super.channelInactive(ctx);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
System.out.println("当前轮询时间:"+new Date());
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.WRITER_IDLE) {
if(currentTime <= TRY_TIMES){
System.out.println("currentTime:"+currentTime);
currentTime++;
ctx.channel().writeAndFlush(HEARTBEAT_SEQUENCE.duplicate());
}
}
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String message = (String) msg;
System.out.println(message);
if (message.equals("Heartbeat")) {
ctx.write("has read message from server");
ctx.flush();
}
ReferenceCountUtil.release(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
public class HeartBeatsClient {
private int port;
private String address;
private EventLoopGroup eventExecutors=new NioEventLoopGroup();
public HeartBeatsClient(int port, String address) {
this.port = port;
this.address = address;
}
public HeartBeatsClient reConnectServer(Bootstrap bootstrap,EventLoopGroup eventExecutors) throws InterruptedException {
System.out.println("lalalall2");
final HeartBeatsClient heartBeatsClient=this;
synchronized (this){
bootstrap.group(eventExecutors).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline channelPipeline=socketChannel.pipeline();
channelPipeline.addLast(new IdleStateHandler(0, 3, 0, TimeUnit.SECONDS));
channelPipeline.addLast(new StringDecoder());
channelPipeline.addLast(new StringEncoder());
channelPipeline.addLast(new HeartBeatClientHandler(heartBeatsClient));
}
});
}
bootstrap.remoteAddress(address, port);
ChannelFuture channelFuture=bootstrap.connect().sync();
channelFuture.addListener(new ConnectListener(this));
channelFuture.channel().closeFuture().sync();
return this;
}
public void start() throws InterruptedException {
reConnectServer(new Bootstrap(),eventExecutors);
}
}
public class ConnectListener implements ChannelFutureListener {
private HeartBeatsClient heartBeatsClient;
public ConnectListener(HeartBeatsClient heartBeatsClient){
this.heartBeatsClient=heartBeatsClient;
}
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (!channelFuture.isSuccess()){
System.out.println("将进行重连");
final EventLoop loop = channelFuture.channel().eventLoop();
loop.schedule(new Runnable() {
public void run() {
try {
heartBeatsClient.reConnectServer(new Bootstrap(),loop);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},1, TimeUnit.SECONDS);
}else {
System.out.println("连接成功");
}
}
}
IdleStateHandler类需要放在第一个加入ChannelPipline里面,否则会不成功,Demo下载