基于netty的心跳检测
来源:互联网 发布:淘宝一键复制有坏处吗 编辑:程序博客网 时间:2024/05/22 10:25
实现的功能:
服务端代码:
参考网站:http://coder.beitown.com/archives/1180
1.客户端网络空闲5秒没有进行写操作是,进行发送一次ping心跳给服务端;
2.客户端如果在下一个发送ping心跳周期来临时,还没有收到服务端pong的心跳应答,则失败心跳计数器加1;
3.每当客户端收到服务端的pong心跳应答后,失败心跳计数器清零;
4.如果连续超过3次没有收到服务端的心跳回复,则断开当前连接,在5秒后进行重连操作,直到重连成功,否则每隔5秒又会进行重连;
5.服务端网络空闲状态到达6秒后,服务端心跳失败计数器加1;
6.只要收到客户端的ping消息,服务端心跳失败计数器清零;
7.服务端连续3次没有收到客户端的ping消息后,将关闭链路,释放资源,等待客户端重连;
服务端代码:
package com.kg.netty.server;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;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.NioServerSocketChannel;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.TimeUnit;import com.kg.netty.msg.KeepAliveMessage;import com.kg.utils.Constants;import com.kg.utils.Utils;public class KeepAliveServer {// 端口private int port ;public KeepAliveServer(int port) {this.port = port;}ChannelFuture f ;ServerBootstrap b ;// 设置6秒检测chanel是否接受过心跳数据private static final int READ_WAIT_SECONDS = 6;// 定义客户端没有收到服务端的pong消息的最大次数private static final int MAX_UN_REC_PING_TIMES = 3;public void startServer() {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {b = new ServerBootstrap();b.group(bossGroup, workerGroup);b.channel(NioServerSocketChannel.class);b.childHandler(new KeepAliveServerInitializer());// 服务器绑定端口监听f = b.bind(port).sync();// 监听服务器关闭监听,此方法会阻塞f.channel().closeFuture().sync();// 可以简写为/* b.bind(portNumber).sync().channel().closeFuture().sync(); */} catch (InterruptedException e) {e.printStackTrace();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}/** * 消息处理器 * @author cullen edward */private class KeepAliveServerInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();/* * 使用ObjectDecoder和ObjectEncoder * 因为双向都有写数据和读数据,所以这里需要两个都设置 * 如果只读,那么只需要ObjectDecoder即可 */pipeline.addLast("decoder", new ObjectDecoder(ClassResolvers.cacheDisabled(this.getClass().getClassLoader())));pipeline.addLast("encoder", new ObjectEncoder());/* * 这里只监听读操作 * 可以根据需求,监听写操作和总得操作 */pipeline.addLast("pong", new IdleStateHandler(READ_WAIT_SECONDS, 0, 0,TimeUnit.SECONDS));pipeline.addLast("handler", new Heartbeat());}}private class Heartbeat extends SimpleChannelInboundHandler<KeepAliveMessage> { // 失败计数器:未收到client端发送的ping请求private int unRecPingTimes = 0 ;// 每个chanel对应一个线程,此处用来存储对应于每个线程的一些基础数据,此处不一定要为KeepAliveMessage对象ThreadLocal<KeepAliveMessage> localMsgInfo = new ThreadLocal<KeepAliveMessage>(); @Overrideprotected void channelRead0(ChannelHandlerContext ctx, KeepAliveMessage msg) throws Exception {System.out.println(ctx.channel().remoteAddress() + " Say : sn=" + msg.getSn()+",reqcode="+msg.getReqCode()); // 收到ping消息后,回复if(Utils.notEmpty(msg.getSn())&&msg.getReqCode()==1){msg.setReqCode(Constants.RET_CODE);ctx.channel().writeAndFlush(msg);// 失败计数器清零unRecPingTimes = 0;if(localMsgInfo.get()==null){KeepAliveMessage localMsg = new KeepAliveMessage();localMsg.setSn(msg.getSn());localMsgInfo.set(localMsg);/* * 这里可以将设备号放入一个集合中进行统一管理 */// TODO}}else{ctx.channel().close();}}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 读超时)"); // 失败计数器次数大于等于3次的时候,关闭链接,等待client重连 if(unRecPingTimes >= MAX_UN_REC_PING_TIMES){ System.out.println("===服务端===(读超时,关闭chanel)"); // 连续超过N次未收到client的ping消息,那么关闭该通道,等待client重连 ctx.channel().close(); }else{ // 失败计数器加1 unRecPingTimes++; } } else if (event.state() == IdleState.WRITER_IDLE) { /*写超时*/ System.out.println("===服务端===(WRITER_IDLE 写超时)"); } else if (event.state() == IdleState.ALL_IDLE) { /*总超时*/ System.out.println("===服务端===(ALL_IDLE 总超时)"); } } } @Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("错误原因:"+cause.getMessage()); if(localMsgInfo.get()!=null){/* * 从管理集合中移除设备号等唯一标示,标示设备离线 */ // TODO } ctx.channel().close();}@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("Client active "); super.channelActive(ctx); }@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {// 关闭,等待重连 ctx.close();if(localMsgInfo.get()!=null){/* * 从管理集合中移除设备号等唯一标示,标示设备离线 */ // TODO } System.out.println("===服务端===(客户端失效)");}}public void stopServer(){if(f!=null){f.channel().close();}}/** * @param args */public static void main(String[] args) {KeepAliveServer keepAliveServer = new KeepAliveServer(1666);keepAliveServer.startServer();}}
客户端代码:
package com.kg.netty.client;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;import com.kg.netty.msg.KeepAliveMessage;import com.kg.utils.Constants;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;@Overridepublic 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();}}
参考网站:http://coder.beitown.com/archives/1180
摘抄自:http://www.aichengxu.com/view/46626
下载地址:
0 0
- 基于netty的心跳检测
- 基于netty的心跳检测
- 基于netty的心跳检测
- netty的心跳检测实现
- netty的心跳检测实现
- netty 心跳检测
- Netty心跳检测
- 基于Netty的文件上传下载、心跳检测、在线聊天的demo
- 基于netty的网络聊天室(二)——心跳检测及断线重连
- 基于netty的网络聊天室(二)——心跳检测及断线重连
- Netty数据通信和心跳检测
- Netty心跳检测(1)
- 基于Netty的RPC架构笔记5之心跳
- netty 5实现长连接心跳检测
- Netty心跳检测篇之IdleStateHandler
- Netty心跳检测篇之简单实例
- Netty(三)文件上传下载、心跳检测
- Netty学习(八)-Netty的心跳机制
- 51nod 1629 B君的圆锥(三分)
- 线性回归与贝叶斯推理——漫谈机器学习
- POJ1330 LCA/给树的节点标记层数
- C#字符转之UTF8转成Unicode
- 清除浮动的几种常用的方法
- 基于netty的心跳检测
- LeetCode 387. First Unique Character in a String
- 【矩阵相关】[Codeforces - 736D]Permutations
- APICLOUD平台初体验(一个任务系统的开发)
- linux下IO相关知识
- 报数(类似猴子选大王)
- POJ 2502 - Subway(单源最短路)
- 在不损失数据的情况下如何扩展C盘
- 剑指offer——面试题28:字符串的排列