package com.lyncc.netty.keepalive;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class KeepAliveClient {
private String host ;
private int port ;
private EventLoopGroup group ;
private Bootstrap b ;
private Channel ch ;
// 定义客户端没有收到服务端的pong消息的最大次数
private static final int MAX_UN_REC_PONG_TIMES = 3;
// 多长时间未请求后,发送心跳
private static final int WRITE_WAIT_SECONDS = 5;
// 隔N秒后重连
private static final int RE_CONN_WAIT_SECONDS = 5;
// 客户端连续N次没有收到服务端的pong消息 计数器
private int unRecPongTimes = 0 ;
private ScheduledExecutorService executorService ;
// 是否停止
private boolean isStop = false ;
public KeepAliveClient(String host, int port) {
this.host = host ;
this.port = port ;
group = new NioEventLoopGroup();
b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).handler(new HeartbeatInitializer());
}
public void start() {
connServer();
}
private void connServer(){
isStop = false;
if(executorService!=null){
executorService.shutdown();
}
executorService = Executors.newScheduledThreadPool(1);
executorService.scheduleWithFixedDelay(new Runnable() {
boolean isConnSucc = true;
public void run() {
try {
// 重置计数器
unRecPongTimes = 0;
// 连接服务端
if(ch!=null&&ch.isOpen()){
ch.close();
}
ch = b.connect(host, port).sync().channel();
// 此方法会阻塞
// ch.closeFuture().sync();
System.out.println("connect server finish");
} catch (Exception e) {
e.printStackTrace();
isConnSucc = false ;
} finally{
if(isConnSucc){
if(executorService!=null){
executorService.shutdown();
}
}
}
}
}, RE_CONN_WAIT_SECONDS, RE_CONN_WAIT_SECONDS, TimeUnit.SECONDS);
}
public class HeartbeatInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new ObjectDecoder(ClassResolvers.cacheDisabled(this.getClass().getClassLoader())));
pipeline.addLast("encoder", new ObjectEncoder());
pipeline.addLast("ping", new IdleStateHandler(0, WRITE_WAIT_SECONDS, 0,TimeUnit.SECONDS));
// 客户端的逻辑
pipeline.addLast("handler", new ClientHandler());
}
}
public class ClientHandler extends SimpleChannelInboundHandler<KeepAliveMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, KeepAliveMessage msg)
throws Exception {
System.out.println("Server say : sn=" + msg.getSn()+",reqcode="+msg.getReqCode());
if (Constants.RET_CODE == msg.getReqCode()) {
// 计数器清零
unRecPongTimes = 0;
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client active ");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client close ");
super.channelInactive(ctx);
/*
* 重连
*/
if(!isStop){
connServer();
}
}
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.READER_IDLE) {
/*读超时*/
System.out.println("===服务端===(READER_IDLE 读超时)");
} else if (event.state() == IdleState.WRITER_IDLE) {
/*写超时*/
System.out.println("===服务端===(WRITER_IDLE 写超时)");
if(unRecPongTimes < MAX_UN_REC_PONG_TIMES){
ctx.channel().writeAndFlush(getSrcMsg()) ;
unRecPongTimes++;
}else{
ctx.channel().close();
}
} else if (event.state() == IdleState.ALL_IDLE) {
/*总超时*/
System.out.println("===服务端===(ALL_IDLE 总超时)");
}
}
}
}
private KeepAliveMessage getSrcMsg(){
KeepAliveMessage keepAliveMessage = new KeepAliveMessage();
// 设备码
keepAliveMessage.setSn("sn_123456abcdfef");
keepAliveMessage.setReqCode(Constants.REQ_CODE);
return keepAliveMessage ;
}
public void stop(){
isStop = true;
if(ch!=null&&ch.isOpen()){
ch.close();
}
if(executorService!=null){
executorService.shutdown();
}
}
/**
* @param args
*/
public static void main(String[] args) {
KeepAliveClient keepAliveServer = new KeepAliveClient("127.0.0.1",1666);
keepAliveServer.start();
}
}